feat: implement heavy governance cost recovery #242

Merged
ahmido merged 1 commits from 209-heavy-governance-cost into dev 2026-04-17 13:17:13 +00:00
32 changed files with 3439 additions and 311 deletions

View File

@ -195,6 +195,7 @@ ## Active Technologies
- PHP 8.4.15 + Laravel 12, Pest v4, PHPUnit 12, Filament v5, Livewire v4, Laravel Sail (207-shared-test-fixture-slimming)
- SQLite `:memory:` for the default test environment, isolated PostgreSQL coverage via the existing dedicated suite, and lane-measurement artifacts under the app-root contract path `storage/logs/test-lanes` (207-shared-test-fixture-slimming)
- SQLite `:memory:` for the default test environment, existing lane artifacts under the app-root contract path `storage/logs/test-lanes`, and no new product persistence (208-heavy-suite-segmentation)
- SQLite `:memory:` for the default test environment, mixed database strategy for some heavy-governance families as declared in `TestLaneManifest`, and existing lane artifacts under the app-root contract path `storage/logs/test-lanes` (209-heavy-governance-cost)
- PHP 8.4.15 (feat/005-bulk-operations)
@ -229,8 +230,8 @@ ## Code Style
PHP 8.4.15: Follow standard conventions
## Recent Changes
- 209-heavy-governance-cost: Added PHP 8.4.15 + Laravel 12, Pest v4, PHPUnit 12, Filament v5, Livewire v4, Laravel Sail
- 208-heavy-suite-segmentation: Added PHP 8.4.15 + Laravel 12, Pest v4, PHPUnit 12, Filament v5, Livewire v4, Laravel Sail
- 207-shared-test-fixture-slimming: Added PHP 8.4.15 + Laravel 12, Pest v4, PHPUnit 12, Filament v5, Livewire v4, Laravel Sail
- 206-test-suite-governance: Added PHP 8.4.15 + Laravel 12, Pest v4, PHPUnit 12, Pest Browser plugin, Filament v5, Livewire v4, Laravel Sail
<!-- MANUAL ADDITIONS START -->
<!-- MANUAL ADDITIONS END -->

View File

@ -4,9 +4,6 @@
use App\Filament\Pages\BaselineCompareLanding;
use App\Filament\Resources\BaselineProfileResource\Pages\ViewBaselineProfile;
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineTenantAssignment;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Facades\Filament;
use Illuminate\Support\Facades\DB;
@ -16,22 +13,7 @@
[$readonlyUser, $tenant] = createUserWithTenant(role: 'readonly');
[$ownerUser] = createUserWithTenant(tenant: $tenant, role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$snapshot = BaselineSnapshot::factory()->complete()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
[$profile] = seedActiveBaselineForTenant($tenant);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -50,22 +32,7 @@
[$readonlyUser, $tenant] = createUserWithTenant(role: 'readonly');
[$ownerUser] = createUserWithTenant(tenant: $tenant, role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$snapshot = BaselineSnapshot::factory()->complete()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
seedActiveBaselineForTenant($tenant);
$tenant->makeCurrent();
Filament::setTenant($tenant, true);
@ -83,22 +50,7 @@
[$readonlyUser, $tenant] = createUserWithTenant(role: 'readonly');
[$ownerUser] = createUserWithTenant(tenant: $tenant, role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$snapshot = BaselineSnapshot::factory()->complete()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
[$profile] = seedActiveBaselineForTenant($tenant);
DB::table('baseline_profiles')
->where('id', (int) $profile->getKey())

View File

@ -7,6 +7,7 @@
use App\Jobs\CaptureBaselineSnapshotJob;
use App\Models\BaselineProfile;
use App\Models\OperationRun;
use App\Models\Tenant;
use App\Support\Baselines\BaselineCaptureMode;
use App\Support\Workspaces\WorkspaceContext;
use Filament\Actions\Action;
@ -30,12 +31,21 @@ function baselineProfileCaptureHeaderActions(Testable $component): array
return $instance->getCachedHeaderActions();
}
function seedCaptureProfileForTenant(
Tenant $tenant,
BaselineCaptureMode $captureMode = BaselineCaptureMode::FullContent,
array $attributes = [],
): BaselineProfile {
return BaselineProfile::factory()->active()->create(array_merge([
'workspace_id' => (int) $tenant->workspace_id,
'capture_mode' => $captureMode->value,
], $attributes));
}
it('redirects unauthenticated users (302) when accessing the capture start surface', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -47,9 +57,7 @@ function baselineProfileCaptureHeaderActions(Testable $component): array
[$user, $tenant] = createUserWithTenant(role: 'owner');
[$otherUser, $otherTenant] = createUserWithTenant(role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);
session()->put(WorkspaceContext::SESSION_KEY, (int) $otherTenant->workspace_id);
@ -63,9 +71,7 @@ function baselineProfileCaptureHeaderActions(Testable $component): array
[$user, $tenant] = createUserWithTenant(role: 'readonly');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -86,10 +92,7 @@ function baselineProfileCaptureHeaderActions(Testable $component): array
[$user, $tenant] = createUserWithTenant(role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'capture_mode' => BaselineCaptureMode::FullContent->value,
]);
$profile = seedCaptureProfileForTenant($tenant);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -129,10 +132,7 @@ function baselineProfileCaptureHeaderActions(Testable $component): array
[$user, $tenant] = createUserWithTenant(role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'capture_mode' => BaselineCaptureMode::FullContent->value,
]);
$profile = seedCaptureProfileForTenant($tenant);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -152,8 +152,7 @@ function baselineProfileCaptureHeaderActions(Testable $component): array
it('shows readiness copy without exposing raw canonical scope json on the capture start surface', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
$profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic, [
'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []],
]);
@ -172,9 +171,7 @@ function baselineProfileCaptureHeaderActions(Testable $component): array
[$user, $tenant] = createUserWithTenant(role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$profile = seedCaptureProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);
DB::table('baseline_profiles')
->where('id', (int) $profile->getKey())

View File

@ -8,6 +8,7 @@
use App\Models\BaselineProfile;
use App\Models\BaselineSnapshot;
use App\Models\BaselineTenantAssignment;
use App\Models\Tenant;
use App\Models\OperationRun;
use App\Support\Baselines\BaselineCaptureMode;
use App\Support\Baselines\Compare\CompareStrategyRegistry;
@ -34,29 +35,27 @@ function baselineProfileHeaderActions(Testable $component): array
return $instance->getCachedHeaderActions();
}
/**
* @return array{0: BaselineProfile, 1: BaselineSnapshot}
*/
function seedComparableBaselineProfileForTenant(Tenant $tenant, BaselineCaptureMode $captureMode = BaselineCaptureMode::FullContent): array
{
[$profile, $snapshot] = seedActiveBaselineForTenant($tenant);
$profile->forceFill([
'capture_mode' => $captureMode->value,
])->save();
return [$profile->fresh(), $snapshot];
}
it('does not start baseline compare for workspace members missing tenant.sync', function (): void {
Queue::fake();
config()->set('tenantpilot.baselines.full_content_capture.enabled', true);
[$user, $tenant] = createUserWithTenant(role: 'readonly');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'capture_mode' => BaselineCaptureMode::FullContent->value,
]);
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
[$profile] = seedComparableBaselineProfileForTenant($tenant);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -77,23 +76,7 @@ function baselineProfileHeaderActions(Testable $component): array
[$user, $tenant] = createUserWithTenant(role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'capture_mode' => BaselineCaptureMode::FullContent->value,
]);
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
[$profile] = seedComparableBaselineProfileForTenant($tenant);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -123,23 +106,7 @@ function baselineProfileHeaderActions(Testable $component): array
[$user, $tenant] = createUserWithTenant(role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
'capture_mode' => BaselineCaptureMode::FullContent->value,
]);
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
[$profile] = seedComparableBaselineProfileForTenant($tenant);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -167,7 +134,9 @@ function baselineProfileHeaderActions(Testable $component): array
app(FakeCompareStrategy::class),
]));
$profile = BaselineProfile::factory()->active()->create([
[$profile] = seedComparableBaselineProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);
$profile->forceFill([
'workspace_id' => (int) $tenant->workspace_id,
'scope_jsonb' => [
'version' => 2,
@ -186,20 +155,7 @@ function baselineProfileHeaderActions(Testable $component): array
],
],
],
]);
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
])->save();
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -218,22 +174,7 @@ function baselineProfileHeaderActions(Testable $component): array
[$user, $tenant] = createUserWithTenant(role: 'owner');
$this->actingAs($user);
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
[$profile] = seedComparableBaselineProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -268,22 +209,7 @@ function baselineProfileHeaderActions(Testable $component): array
it('keeps compare-assigned-tenants visible but disabled for readonly workspace members after the navigation move', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'readonly');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
[$profile] = seedComparableBaselineProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -299,23 +225,11 @@ function baselineProfileHeaderActions(Testable $component): array
it('shows readiness copy without exposing raw canonical scope json on the compare start surface', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
[$profile] = seedComparableBaselineProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);
$profile->forceFill([
'scope_jsonb' => ['policy_types' => ['deviceConfiguration'], 'foundation_types' => []],
]);
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
])->save();
session()->put(WorkspaceContext::SESSION_KEY, (int) $tenant->workspace_id);
@ -332,22 +246,7 @@ function baselineProfileHeaderActions(Testable $component): array
[$user, $tenant] = createUserWithTenant(role: 'owner');
$profile = BaselineProfile::factory()->active()->create([
'workspace_id' => (int) $tenant->workspace_id,
]);
$snapshot = BaselineSnapshot::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'baseline_profile_id' => (int) $profile->getKey(),
]);
$profile->update(['active_snapshot_id' => (int) $snapshot->getKey()]);
BaselineTenantAssignment::factory()->create([
'workspace_id' => (int) $tenant->workspace_id,
'tenant_id' => (int) $tenant->getKey(),
'baseline_profile_id' => (int) $profile->getKey(),
]);
[$profile] = seedComparableBaselineProfileForTenant($tenant, BaselineCaptureMode::Opportunistic);
DB::table('baseline_profiles')
->where('id', (int) $profile->getKey())

View File

@ -12,29 +12,41 @@
uses(RefreshDatabase::class);
function findingBulkAuditResourceIds(int $tenantId, string $action): array
{
return AuditLog::query()
->where('tenant_id', $tenantId)
->where('action', $action)
->pluck('resource_id')
->map(static fn (string $resourceId): int => (int) $resourceId)
->sort()
->values()
->all();
}
it('supports bulk workflow actions and audits each record', function (): void {
[$manager, $tenant] = createUserWithTenant(role: 'manager');
$this->actingAs($manager);
Filament::setTenant($tenant, true);
$findings = Finding::factory()
->count(101)
->count(6)
->for($tenant)
->create([
'status' => Finding::STATUS_NEW,
'triaged_at' => null,
]);
Livewire::test(ListFindings::class)
$component = Livewire::test(ListFindings::class);
$component
->callTableBulkAction('triage_selected', $findings)
->assertHasNoTableBulkActionErrors();
$findings->each(fn (Finding $finding) => expect($finding->refresh()->status)->toBe(Finding::STATUS_TRIAGED));
expect(AuditLog::query()
->where('tenant_id', (int) $tenant->getKey())
->where('action', 'finding.triaged')
->count())->toBe(101);
expect(findingBulkAuditResourceIds((int) $tenant->getKey(), 'finding.triaged'))
->toEqual($findings->pluck('id')->sort()->values()->all());
$assignee = User::factory()->create();
createUserWithTenant(tenant: $tenant, user: $assignee, role: 'operator');
@ -48,7 +60,7 @@
'owner_user_id' => null,
]);
Livewire::test(ListFindings::class)
$component
->callTableBulkAction('assign_selected', $assignFindings, data: [
'assignee_user_id' => (int) $assignee->getKey(),
'owner_user_id' => (int) $manager->getKey(),
@ -62,10 +74,8 @@
->and((int) $finding->owner_user_id)->toBe((int) $manager->getKey());
});
expect(AuditLog::query()
->where('tenant_id', (int) $tenant->getKey())
->where('action', 'finding.assigned')
->count())->toBe(3);
expect(findingBulkAuditResourceIds((int) $tenant->getKey(), 'finding.assigned'))
->toEqual($assignFindings->pluck('id')->sort()->values()->all());
$resolveFindings = Finding::factory()
->count(2)
@ -76,7 +86,7 @@
'resolved_reason' => null,
]);
Livewire::test(ListFindings::class)
$component
->callTableBulkAction('resolve_selected', $resolveFindings, data: [
'resolved_reason' => 'fixed',
])
@ -90,10 +100,8 @@
->and($finding->resolved_at)->not->toBeNull();
});
expect(AuditLog::query()
->where('tenant_id', (int) $tenant->getKey())
->where('action', 'finding.resolved')
->count())->toBe(2);
expect(findingBulkAuditResourceIds((int) $tenant->getKey(), 'finding.resolved'))
->toEqual($resolveFindings->pluck('id')->sort()->values()->all());
$closeFindings = Finding::factory()
->count(2)
@ -104,7 +112,7 @@
'closed_reason' => null,
]);
Livewire::test(ListFindings::class)
$component
->callTableBulkAction('close_selected', $closeFindings, data: [
'closed_reason' => 'not applicable',
])
@ -119,9 +127,6 @@
->and($finding->closed_by_user_id)->not->toBeNull();
});
expect(AuditLog::query()
->where('tenant_id', (int) $tenant->getKey())
->where('action', 'finding.closed')
->count())->toBe(2);
expect(findingBulkAuditResourceIds((int) $tenant->getKey(), 'finding.closed'))
->toEqual($closeFindings->pluck('id')->sort()->values()->all());
});

View File

@ -19,6 +19,38 @@
uses(RefreshDatabase::class);
/**
* @return array{0: User, 1: User, 2: \App\Models\Tenant, 3: Finding, 4: FindingExceptionService, 5: FindingException}
*/
function seedApprovedFindingExceptionWindow(): array
{
[$requester, $tenant] = createUserWithTenant(role: 'owner');
$approver = User::factory()->create();
createUserWithTenant(tenant: $tenant, user: $approver, role: 'owner', workspaceRole: 'manager');
$finding = Finding::factory()->for($tenant)->create([
'status' => Finding::STATUS_RISK_ACCEPTED,
]);
/** @var FindingExceptionService $service */
$service = app(FindingExceptionService::class);
$requested = $service->request($finding, $tenant, $requester, [
'owner_user_id' => (int) $requester->getKey(),
'request_reason' => 'Initial acceptance window',
'review_due_at' => now()->addDays(5)->toDateTimeString(),
'expires_at' => now()->addDays(20)->toDateTimeString(),
]);
$active = $service->approve($requested, $approver, [
'effective_from' => now()->subDay()->toDateTimeString(),
'expires_at' => now()->addDays(20)->toDateTimeString(),
'approval_reason' => 'Initial approval',
]);
return [$requester, $approver, $tenant, $finding, $service, $active];
}
it('requests and approves a renewal while preserving prior decision history and evidence references', function (): void {
[$requester, $tenant] = createUserWithTenant(role: 'owner');
$approver = User::factory()->create();
@ -156,29 +188,7 @@
});
it('rejects a pending renewal without erasing the prior approved governance window', function (): void {
[$requester, $tenant] = createUserWithTenant(role: 'owner');
$approver = User::factory()->create();
createUserWithTenant(tenant: $tenant, user: $approver, role: 'owner', workspaceRole: 'manager');
$finding = Finding::factory()->for($tenant)->create([
'status' => Finding::STATUS_RISK_ACCEPTED,
]);
/** @var FindingExceptionService $service */
$service = app(FindingExceptionService::class);
$requested = $service->request($finding, $tenant, $requester, [
'owner_user_id' => (int) $requester->getKey(),
'request_reason' => 'Initial acceptance window',
'review_due_at' => now()->addDays(5)->toDateTimeString(),
'expires_at' => now()->addDays(20)->toDateTimeString(),
]);
$active = $service->approve($requested, $approver, [
'effective_from' => now()->subDay()->toDateTimeString(),
'expires_at' => now()->addDays(20)->toDateTimeString(),
'approval_reason' => 'Initial approval',
]);
[$requester, $approver, $tenant, $finding, $service, $active] = seedApprovedFindingExceptionWindow();
$service->renew($active, $requester, [
'owner_user_id' => (int) $requester->getKey(),

View File

@ -23,7 +23,9 @@
'status' => Finding::STATUS_NEW,
]);
Livewire::test(ListFindings::class)
$component = Livewire::test(ListFindings::class);
$component
->callTableAction('triage', $finding)
->assertHasNoTableActionErrors();
@ -31,7 +33,7 @@
expect($finding->status)->toBe(Finding::STATUS_TRIAGED)
->and($finding->triaged_at)->not->toBeNull();
Livewire::test(ListFindings::class)
$component
->callTableAction('start_progress', $finding)
->assertHasNoTableActionErrors();
@ -39,7 +41,7 @@
expect($finding->status)->toBe(Finding::STATUS_IN_PROGRESS)
->and($finding->in_progress_at)->not->toBeNull();
Livewire::test(ListFindings::class)
$component
->callTableAction('resolve', $finding, [
'resolved_reason' => 'patched',
])
@ -50,7 +52,7 @@
->and($finding->resolved_reason)->toBe('patched')
->and($finding->resolved_at)->not->toBeNull();
Livewire::test(ListFindings::class)
$component
->filterTable('open', false)
->callTableAction('reopen', $finding, [
'reopen_reason' => 'The issue recurred in a later scan.',
@ -76,13 +78,15 @@
'status' => Finding::STATUS_NEW,
]);
Livewire::test(ListFindings::class)
$component = Livewire::test(ListFindings::class);
$component
->callTableAction('close', $closeFinding, [
'closed_reason' => 'duplicate ticket',
])
->assertHasNoTableActionErrors();
Livewire::test(ListFindings::class)
$component
->callTableAction('request_exception', $exceptionFinding, [
'owner_user_id' => (int) $user->getKey(),
'request_reason' => 'accepted by security',
@ -117,7 +121,9 @@
'status' => Finding::STATUS_NEW,
]);
Livewire::test(ListFindings::class)
$component = Livewire::test(ListFindings::class);
$component
->callTableAction('assign', $finding, [
'assignee_user_id' => (int) $assignee->getKey(),
'owner_user_id' => (int) $manager->getKey(),
@ -128,7 +134,7 @@
expect((int) $finding->assignee_user_id)->toBe((int) $assignee->getKey())
->and((int) $finding->owner_user_id)->toBe((int) $manager->getKey());
Livewire::test(ListFindings::class)
$component
->callTableAction('assign', $finding, [
'assignee_user_id' => (int) $outsider->getKey(),
'owner_user_id' => (int) $manager->getKey(),

View File

@ -54,7 +54,9 @@
$finding = Finding::factory()->for($tenant)->create(['status' => Finding::STATUS_NEW]);
Livewire::test(ViewFinding::class, ['record' => $finding->getKey()])
$component = Livewire::test(ViewFinding::class, ['record' => $finding->getKey()]);
$component
->callAction('triage')
->assertHasNoActionErrors()
->callAction('assign', [

View File

@ -11,6 +11,16 @@
uses(RefreshDatabase::class);
function actingAsFindingsManagerForFilters(): array
{
[$user, $tenant] = createUserWithTenant(role: 'manager');
test()->actingAs($user);
Filament::setTenant($tenant, true);
return [$user, $tenant];
}
function findingFilterIndicatorLabels($component): array
{
return collect($component->instance()->getTable()->getFilterIndicators())
@ -19,9 +29,7 @@ function findingFilterIndicatorLabels($component): array
}
it('filters findings by overdue quick filter using open statuses only', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'manager');
$this->actingAs($user);
Filament::setTenant($tenant, true);
[, $tenant] = actingAsFindingsManagerForFilters();
$overdueOpen = Finding::factory()->for($tenant)->create([
'status' => Finding::STATUS_NEW,
@ -45,14 +53,11 @@ function findingFilterIndicatorLabels($component): array
});
it('filters findings by workflow family and governance validity', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'manager');
[, $tenant] = actingAsFindingsManagerForFilters();
[$requester] = createUserWithTenant(tenant: $tenant, role: 'owner');
$approver = User::factory()->create();
createUserWithTenant(tenant: $tenant, user: $approver, role: 'owner', workspaceRole: 'manager');
$this->actingAs($user);
Filament::setTenant($tenant, true);
$active = Finding::factory()->for($tenant)->create([
'status' => Finding::STATUS_NEW,
]);
@ -105,9 +110,7 @@ function findingFilterIndicatorLabels($component): array
});
it('filters findings by high severity quick filter', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'manager');
$this->actingAs($user);
Filament::setTenant($tenant, true);
[, $tenant] = actingAsFindingsManagerForFilters();
$critical = Finding::factory()->for($tenant)->create([
'severity' => Finding::SEVERITY_CRITICAL,
@ -131,9 +134,7 @@ function findingFilterIndicatorLabels($component): array
});
it('filters findings by my assigned quick filter', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'manager');
$this->actingAs($user);
Filament::setTenant($tenant, true);
[$user, $tenant] = actingAsFindingsManagerForFilters();
$otherUser = User::factory()->create();
createUserWithTenant(tenant: $tenant, user: $otherUser, role: 'operator');
@ -160,9 +161,7 @@ function findingFilterIndicatorLabels($component): array
});
it('persists findings search, sort, and filter state while keeping the resource pagination profile', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'manager');
$this->actingAs($user);
Filament::setTenant($tenant, true);
actingAsFindingsManagerForFilters();
$component = Livewire::test(ListFindings::class)
->searchTable('drift')
@ -182,9 +181,7 @@ function findingFilterIndicatorLabels($component): array
});
it('composes status and created date filters with active indicators', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'manager');
$this->actingAs($user);
Filament::setTenant($tenant, true);
[, $tenant] = actingAsFindingsManagerForFilters();
$matching = Finding::factory()->for($tenant)->create([
'status' => Finding::STATUS_NEW,
@ -214,9 +211,7 @@ function findingFilterIndicatorLabels($component): array
});
it('clears findings filters back to the default open view', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'manager');
$this->actingAs($user);
Filament::setTenant($tenant, true);
[, $tenant] = actingAsFindingsManagerForFilters();
$openFinding = Finding::factory()->for($tenant)->create([
'status' => Finding::STATUS_NEW,
@ -250,9 +245,7 @@ function findingFilterIndicatorLabels($component): array
});
it('prefilters dashboard open-drift drill-throughs to the named findings subset', function (): void {
[$user, $tenant] = createUserWithTenant(role: 'manager');
$this->actingAs($user);
Filament::setTenant($tenant, true);
[, $tenant] = actingAsFindingsManagerForFilters();
$openDrift = Finding::factory()->for($tenant)->create([
'finding_type' => Finding::FINDING_TYPE_DRIFT,

View File

@ -126,9 +126,19 @@
use Illuminate\Foundation\Testing\RefreshDatabase;
use Illuminate\Support\Facades\Queue;
use Livewire\Livewire;
use Tests\Support\TestLaneManifest;
uses(RefreshDatabase::class);
it('keeps the retained action-surface contract family anchored in heavy-governance inventory', function (): void {
$inventoryRecord = collect(TestLaneManifest::heavyGovernanceHotspotInventory())
->firstWhere('familyId', 'action-surface-contract');
expect($inventoryRecord)->not->toBeNull()
->and($inventoryRecord['classificationId'])->toBe('surface-guard')
->and($inventoryRecord['status'])->toBe('retained');
});
function actionSurfaceSystemPanelContext(array $capabilities): PlatformUser
{
Filament::setCurrentPanel('system');

View File

@ -55,8 +55,16 @@
it('defines lane, classification, and family budget targets for heavy-governance attribution', function (): void {
$budgetTargets = collect(TestLaneManifest::budgetTargets());
$contract = TestLaneManifest::heavyGovernanceBudgetContract();
$laneBudgetTarget = $budgetTargets
->first(static fn (array $target): bool => $target['targetType'] === 'lane' && $target['targetId'] === 'heavy-governance');
expect($budgetTargets->contains(static fn (array $target): bool => $target['targetType'] === 'lane' && $target['targetId'] === 'heavy-governance'))->toBeTrue()
expect($laneBudgetTarget)->not->toBeNull()
->and($contract['summaryThresholdSeconds'])->toBe(300.0)
->and($contract['evaluationThresholdSeconds'])->toBe(200.0)
->and($contract['normalizedThresholdSeconds'])->toBeGreaterThanOrEqual(300.0)
->and($laneBudgetTarget['thresholdSeconds'])->toBe($contract['normalizedThresholdSeconds'])
->and($laneBudgetTarget['lifecycleState'])->toBe($contract['lifecycleState'])
->and($budgetTargets->contains(static fn (array $target): bool => $target['targetType'] === 'classification' && $target['targetId'] === 'surface-guard'))->toBeTrue()
->and($budgetTargets->contains(static fn (array $target): bool => $target['targetType'] === 'classification' && $target['targetId'] === 'discovery-heavy'))->toBeTrue()
->and($budgetTargets->contains(static fn (array $target): bool => $target['targetType'] === 'family' && $target['targetId'] === 'action-surface-contract'))->toBeTrue()
@ -64,6 +72,8 @@
});
it('evaluates heavy-governance budgets against named class and family totals', function (): void {
$currentRunContract = TestLaneManifest::heavyGovernanceBudgetContract(110.0);
$durationsByFile = [
'tests/Feature/Guards/ActionSurfaceContractTest.php' => 31.2,
'tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php' => 17.4,
@ -95,5 +105,7 @@
->toEqualCanonicalizing(['lane', 'classification', 'family'])
->and(collect($report['budgetEvaluations'])->contains(static fn (array $evaluation): bool => $evaluation['targetType'] === 'lane' && $evaluation['targetId'] === 'heavy-governance'))->toBeTrue()
->and(collect($report['budgetEvaluations'])->contains(static fn (array $evaluation): bool => $evaluation['targetType'] === 'classification' && $evaluation['targetId'] === 'surface-guard'))->toBeTrue()
->and(collect($report['budgetEvaluations'])->contains(static fn (array $evaluation): bool => $evaluation['targetType'] === 'family' && $evaluation['targetId'] === 'action-surface-contract'))->toBeTrue();
->and(collect($report['budgetEvaluations'])->contains(static fn (array $evaluation): bool => $evaluation['targetType'] === 'family' && $evaluation['targetId'] === 'action-surface-contract'))->toBeTrue()
->and($report['budgetContract']['normalizedThresholdSeconds'])->toBe($currentRunContract['normalizedThresholdSeconds'])
->and($report['budgetOutcome']['decisionStatus'])->toBe($currentRunContract['decisionStatus']);
});

View File

@ -63,4 +63,34 @@
'tests/Feature/Rbac/OnboardingWizardUiEnforcementTest.php',
'tests/Feature/Filament/BackupSetAdminTenantParityTest.php',
);
});
it('keeps heavy-governance hotspot ownership honest across slimmed, retained, and follow-up families', function (): void {
$inventory = collect(TestLaneManifest::heavyGovernanceHotspotInventory())->keyBy('familyId');
$decomposition = collect(TestLaneManifest::heavyGovernanceDecompositionRecords())->keyBy('familyId');
$decisions = collect(TestLaneManifest::heavyGovernanceSlimmingDecisions())->keyBy('familyId');
$outcome = TestLaneManifest::heavyGovernanceBudgetOutcome();
expect($inventory->keys()->all())->toEqual([
'baseline-profile-start-surfaces',
'action-surface-contract',
'ops-ux-governance',
'findings-workflow-surfaces',
'finding-bulk-actions-workflow',
'workspace-settings-slice-management',
])
->and($inventory->get('baseline-profile-start-surfaces')['status'])->toBe('slimmed')
->and($inventory->get('findings-workflow-surfaces')['status'])->toBe('slimmed')
->and($inventory->get('finding-bulk-actions-workflow')['status'])->toBe('slimmed')
->and($inventory->get('action-surface-contract')['status'])->toBe('retained')
->and($inventory->get('ops-ux-governance')['status'])->toBe('retained')
->and($inventory->get('workspace-settings-slice-management')['status'])->toBe('follow-up')
->and($decomposition->get('finding-bulk-actions-workflow')['recommendedAction'])->toBe('narrow-assertions')
->and($decisions->get('finding-bulk-actions-workflow')['decisionType'])->toBe('trim-duplicate-work')
->and($decisions->get('workspace-settings-slice-management')['decisionType'])->toBe('follow-up')
->and($outcome['remainingOpenFamilies'])->toEqualCanonicalizing([
'action-surface-contract',
'ops-ux-governance',
'workspace-settings-slice-management',
]);
});

View File

@ -4,6 +4,16 @@
use App\Services\Operations\OperationLifecyclePolicyValidator;
use Tests\Support\OpsUx\SourceFileScanner;
use Tests\Support\TestLaneManifest;
it('keeps ops ux governance retained in the heavy-governance inventory', function (): void {
$inventoryRecord = collect(TestLaneManifest::heavyGovernanceHotspotInventory())
->firstWhere('familyId', 'ops-ux-governance');
expect($inventoryRecord)->not->toBeNull()
->and($inventoryRecord['classificationId'])->toBe('surface-guard')
->and($inventoryRecord['status'])->toBe('retained');
});
it('keeps lifecycle bridge ownership and initiator-null notification discipline intact', function (): void {
$validator = app(OperationLifecyclePolicyValidator::class);

View File

@ -60,4 +60,19 @@
->toContain('ui-light', 'ui-workflow', 'surface-guard', 'discovery-heavy')
->and(collect($report['familyAttribution'])->pluck('familyId')->all())
->toContain('baseline-compare-matrix-workflow', 'action-surface-contract', 'backup-set-admin-tenant-parity');
});
it('keeps heavy-governance snapshot artifact paths rooted in the canonical lane directory', function (): void {
$report = TestLaneReport::buildReport(
laneId: 'heavy-governance',
wallClockSeconds: 140.0,
slowestEntries: [],
durationsByFile: [],
);
expect($report['budgetSnapshots'])->toHaveCount(2)
->and($report['budgetSnapshots'][0]['artifactPaths']['summary'])->toStartWith('storage/logs/test-lanes/heavy-governance-')
->and($report['budgetSnapshots'][1]['artifactPaths']['summary'])->toBe('storage/logs/test-lanes/heavy-governance-latest.summary.md')
->and($report['budgetSnapshots'][1]['artifactPaths']['budget'])->toBe('storage/logs/test-lanes/heavy-governance-latest.budget.json')
->and($report['budgetSnapshots'][1]['artifactPaths']['report'])->toBe('storage/logs/test-lanes/heavy-governance-latest.report.json');
});

View File

@ -7,16 +7,16 @@
function heavyGovernanceSyntheticHotspots(): array
{
return [
['file' => 'tests/Feature/Guards/ActionSurfaceContractTest.php', 'seconds' => 31.2],
['file' => 'tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php', 'seconds' => 17.4],
['file' => 'tests/Feature/Filament/PolicyVersionAdminSearchParityTest.php', 'seconds' => 16.1],
['file' => 'tests/Feature/Rbac/BackupItemsRelationManagerUiEnforcementTest.php', 'seconds' => 14.5],
['file' => 'tests/Feature/Rbac/WorkspaceMembershipsRelationManagerUiEnforcementTest.php', 'seconds' => 12.8],
['file' => 'tests/Feature/Filament/TenantReviewHeaderDisciplineTest.php', 'seconds' => 11.7],
['file' => 'tests/Feature/Filament/PanelNavigationSegregationTest.php', 'seconds' => 10.9],
['file' => 'tests/Feature/Filament/Alerts/AlertsKpiHeaderTest.php', 'seconds' => 9.8],
['file' => 'tests/Feature/Guards/OperationLifecycleOpsUxGuardTest.php', 'seconds' => 8.7],
['file' => 'tests/Feature/ProviderConnections/CredentialLeakGuardTest.php', 'seconds' => 7.6],
['file' => 'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php', 'seconds' => 22.4],
['file' => 'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php', 'seconds' => 21.1],
['file' => 'tests/Feature/Filament/BaselineActionAuthorizationTest.php', 'seconds' => 19.5],
['file' => 'tests/Feature/Findings/FindingBulkActionsTest.php', 'seconds' => 18.2],
['file' => 'tests/Feature/Findings/FindingWorkflowRowActionsTest.php', 'seconds' => 14.8],
['file' => 'tests/Feature/Findings/FindingWorkflowViewActionsTest.php', 'seconds' => 13.6],
['file' => 'tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php', 'seconds' => 12.7],
['file' => 'tests/Feature/Guards/ActionSurfaceContractTest.php', 'seconds' => 11.9],
['file' => 'tests/Feature/Guards/OperationLifecycleOpsUxGuardTest.php', 'seconds' => 10.4],
['file' => 'tests/Feature/Filament/PolicyResourceAdminSearchParityTest.php', 'seconds' => 4.2],
];
}
@ -38,7 +38,7 @@ function heavyGovernanceSyntheticHotspots(): array
->and((string) file_get_contents($gitignore))->toContain('!.gitignore');
});
it('publishes heavy attribution and budget payloads under the canonical artifact root', function (): void {
it('publishes heavy attribution, contract, and coverage payloads under the canonical artifact root', function (): void {
$durationsByFile = collect(heavyGovernanceSyntheticHotspots())
->mapWithKeys(static fn (array $entry): array => [$entry['file'] => $entry['seconds']])
->all();
@ -64,13 +64,41 @@ function heavyGovernanceSyntheticHotspots(): array
expect($report['artifactDirectory'])->toBe('storage/logs/test-lanes')
->and($report['slowestEntries'])->toHaveCount(10)
->and($report)->toHaveKeys([
'budgetContract',
'hotspotInventory',
'decompositionRecords',
'slimmingDecisions',
'authorGuidance',
'inventoryCoverage',
'budgetSnapshots',
'budgetOutcome',
'remainingOpenFamilies',
'stabilizedFamilies',
])
->and(collect($report['classificationAttribution'])->pluck('classificationId')->all())
->toContain('surface-guard', 'discovery-heavy')
->toContain('ui-workflow', 'surface-guard', 'discovery-heavy')
->and(collect($report['familyAttribution'])->pluck('familyId')->all())
->toContain('action-surface-contract', 'policy-resource-admin-search-parity', 'ops-ux-governance')
->toContain(
'baseline-profile-start-surfaces',
'findings-workflow-surfaces',
'finding-bulk-actions-workflow',
'workspace-settings-slice-management',
'action-surface-contract',
'ops-ux-governance',
)
->and(collect($report['budgetEvaluations'])->pluck('targetType')->unique()->values()->all())
->toEqualCanonicalizing(['lane', 'classification', 'family'])
->and($report['familyBudgetEvaluations'])->not->toBeEmpty();
->and($report['familyBudgetEvaluations'])->not->toBeEmpty()
->and($report['inventoryCoverage']['meetsInclusionRule'])->toBeTrue()
->and($report['inventoryCoverage']['inventoryFamilyCount'])->toBe(6)
->and($report['budgetSnapshots'])->toHaveCount(2)
->and($report['budgetOutcome'])->toHaveKeys([
'decisionStatus',
'finalThresholdSeconds',
'remainingOpenFamilies',
'followUpDebt',
]);
});
it('publishes the shared fixture slimming comparison only for the governed standard lanes', function (): void {

View File

@ -36,6 +36,23 @@
->and(file_exists(repo_path('scripts/platform-test-report')))->toBeTrue();
});
it('keeps heavy-governance baseline capture support inside the checked-in wrappers', function (): void {
$laneRunner = (string) file_get_contents(repo_path('scripts/platform-test-lane'));
$reportRunner = (string) file_get_contents(repo_path('scripts/platform-test-report'));
expect($laneRunner)->toContain('--capture-baseline', 'copy_heavy_baseline_artifacts', 'heavy-governance-baseline.${suffix}')
->and($reportRunner)->toContain('--capture-baseline', 'copy_heavy_baseline_artifacts', 'heavy-governance-baseline.${suffix}');
});
it('avoids expanding an empty forwarded-argument array in the lane runner', function (): void {
$laneRunner = (string) file_get_contents(repo_path('scripts/platform-test-lane'));
expect($laneRunner)
->toContain('if [[ ${#remaining_args[@]} -gt 0 ]]; then')
->and($laneRunner)->toContain('./vendor/bin/sail composer run --timeout=0 "${COMPOSER_SCRIPT}" -- "${remaining_args[@]}"')
->and($laneRunner)->toContain('./vendor/bin/sail composer run --timeout=0 "${COMPOSER_SCRIPT}"');
});
it('routes the foundational lane commands through stable artisan arguments', function (): void {
$fastFeedbackConfig = TestLaneManifest::laneConfigurationPath('fast-feedback');
$fastFeedbackContents = (string) file_get_contents(TestLaneManifest::absolutePath($fastFeedbackConfig));

View File

@ -4,7 +4,7 @@
use Tests\Support\TestLaneManifest;
it('declares the six checked-in lanes with a single fast-feedback default and the spec 208 metadata surfaces', function (): void {
it('declares the six checked-in lanes with a single fast-feedback default and the spec 208 plus 209 metadata surfaces', function (): void {
$manifest = TestLaneManifest::manifest();
$laneIds = array_column($manifest['lanes'], 'id');
$defaultLanes = array_values(array_filter(
@ -23,6 +23,13 @@
'budgetTargets',
'lanes',
'familyBudgets',
'heavyGovernanceBudgetContract',
'heavyGovernanceHotspotInventory',
'heavyGovernanceDecompositionRecords',
'heavyGovernanceSlimmingDecisions',
'heavyGovernanceBudgetSnapshots',
'heavyGovernanceBudgetOutcome',
'heavyGovernanceAuthorGuidance',
])
->and($laneIds)->toEqualCanonicalizing([
'fast-feedback',
@ -113,4 +120,39 @@
->and($familyBudgets[0])->toHaveKeys(['familyId', 'targetType', 'targetId', 'selectors', 'thresholdSeconds'])
->and(collect($familyBudgets)->pluck('familyId')->all())
->toContain('action-surface-contract', 'browser-smoke', 'baseline-compare-matrix-workflow', 'baseline-profile-start-surfaces', 'drift-bulk-triage-all-matching', 'finding-bulk-actions-workflow', 'findings-workflow-surfaces', 'workspace-only-admin-surface-independence', 'workspace-settings-slice-management');
});
it('publishes the heavy-governance contract, inventory, and guidance surfaces needed for honest rerun review', function (): void {
$contract = TestLaneManifest::heavyGovernanceBudgetContract();
$inventory = collect(TestLaneManifest::heavyGovernanceHotspotInventory());
expect($contract['summaryThresholdSeconds'])->toBe(300.0)
->and($contract['evaluationThresholdSeconds'])->toBe(200.0)
->and($contract['normalizedThresholdSeconds'])->toBeGreaterThanOrEqual(300.0)
->and($contract['decisionStatus'])->toBeIn(['recovered', 'recalibrated'])
->and($inventory)->toHaveCount(6)
->and($inventory->pluck('familyId')->all())->toEqual([
'baseline-profile-start-surfaces',
'action-surface-contract',
'ops-ux-governance',
'findings-workflow-surfaces',
'finding-bulk-actions-workflow',
'workspace-settings-slice-management',
])
->and(collect(TestLaneManifest::heavyGovernanceBudgetSnapshots()))->toHaveCount(2)
->and(TestLaneManifest::heavyGovernanceBudgetOutcome())->toHaveKeys([
'decisionStatus',
'finalThresholdSeconds',
'deltaSeconds',
'remainingOpenFamilies',
'followUpDebt',
])
->and(collect(TestLaneManifest::heavyGovernanceAuthorGuidance())->pluck('ruleId')->all())
->toEqualCanonicalizing([
'heavy-family-reuse-before-creation',
'heavy-family-create-only-for-new-trust',
'split-discovery-workflow-surface-concerns',
'retain-intentional-heavy-depth-explicitly',
'record-helper-or-fixture-residuals',
]);
});

View File

@ -79,4 +79,22 @@
expect(TestLaneManifest::describeFilePlacement('tests/Feature/Baselines/BaselineCompareMatrixBuilderTest.php'))
->toContain('baseline-compare-matrix-workflow', 'ui-workflow', 'Mixed-file');
});
it('publishes heavy-governance author guidance alongside the canonical hotspot family ownership', function (): void {
$inventoryFamilies = collect(TestLaneManifest::heavyGovernanceHotspotInventory())->pluck('familyId')->all();
$guidanceRuleIds = collect(TestLaneManifest::heavyGovernanceAuthorGuidance())->pluck('ruleId')->all();
foreach ($inventoryFamilies as $familyId) {
expect(TestLaneManifest::family($familyId)['targetLaneId'])->toBe('heavy-governance')
->and(TestLaneManifest::family($familyId)['currentLaneId'])->toBeIn(['confidence', 'heavy-governance']);
}
expect($guidanceRuleIds)->toEqualCanonicalizing([
'heavy-family-reuse-before-creation',
'heavy-family-create-only-for-new-trust',
'split-discovery-workflow-surface-concerns',
'retain-intentional-heavy-depth-explicitly',
'record-helper-or-fixture-residuals',
]);
});

View File

@ -119,4 +119,99 @@ public static function evaluateBudgetTargets(
return $evaluations;
}
/**
* @param array<string, mixed> $contract
* @return array<string, int|float|string>
*/
public static function evaluateGovernanceContract(array $contract, float $measuredSeconds): array
{
foreach (['laneId', 'summaryThresholdSeconds', 'evaluationThresholdSeconds', 'normalizedThresholdSeconds'] as $requiredKey) {
if (! array_key_exists($requiredKey, $contract)) {
throw new InvalidArgumentException(sprintf('Governance contracts must define [%s].', $requiredKey));
}
}
$normalizedThresholdSeconds = (float) $contract['normalizedThresholdSeconds'];
if ($normalizedThresholdSeconds <= 0) {
throw new InvalidArgumentException('Governance contracts must define a positive normalizedThresholdSeconds value.');
}
$enforcementLevel = (string) ($contract['enforcementLevel'] ?? 'warn');
$budgetStatus = 'within-budget';
if ($measuredSeconds > $normalizedThresholdSeconds) {
$budgetStatus = in_array($enforcementLevel, ['report-only', 'warn'], true)
? 'warning'
: 'over-budget';
}
return array_filter([
'laneId' => (string) $contract['laneId'],
'summaryThresholdSeconds' => (float) $contract['summaryThresholdSeconds'],
'evaluationThresholdSeconds' => (float) $contract['evaluationThresholdSeconds'],
'normalizedThresholdSeconds' => $normalizedThresholdSeconds,
'baselineSource' => (string) ($contract['baselineSource'] ?? 'measured-lane'),
'enforcementLevel' => $enforcementLevel,
'lifecycleState' => (string) ($contract['lifecycleState'] ?? 'draft'),
'decisionStatus' => (string) ($contract['decisionStatus'] ?? 'pending'),
'measuredSeconds' => round($measuredSeconds, 6),
'budgetStatus' => $budgetStatus,
'reconciliationRationale' => isset($contract['reconciliationRationale']) ? (string) $contract['reconciliationRationale'] : null,
], static fn (mixed $value): bool => $value !== null);
}
/**
* @param array<string, mixed> $baselineSnapshot
* @param array<string, mixed> $currentSnapshot
* @return array<string, float>
*/
public static function compareSnapshots(array $baselineSnapshot, array $currentSnapshot): array
{
$baselineSeconds = round((float) ($baselineSnapshot['wallClockSeconds'] ?? 0.0), 6);
$currentSeconds = round((float) ($currentSnapshot['wallClockSeconds'] ?? 0.0), 6);
$deltaSeconds = round($currentSeconds - $baselineSeconds, 6);
$deltaPercent = $baselineSeconds > 0.0
? round(($deltaSeconds / $baselineSeconds) * 100, 6)
: 0.0;
return [
'baselineSeconds' => $baselineSeconds,
'currentSeconds' => $currentSeconds,
'deltaSeconds' => $deltaSeconds,
'deltaPercent' => $deltaPercent,
];
}
/**
* @param array<string, mixed> $contract
* @param array<string, mixed> $baselineSnapshot
* @param array<string, mixed> $currentSnapshot
* @param list<string> $remainingOpenFamilies
* @param list<string> $followUpDebt
* @return array<string, mixed>
*/
public static function buildOutcomeRecord(
array $contract,
array $baselineSnapshot,
array $currentSnapshot,
array $remainingOpenFamilies,
string $justification,
array $followUpDebt = [],
): array {
$comparison = self::compareSnapshots($baselineSnapshot, $currentSnapshot);
return array_filter([
'outcomeId' => sprintf('%s-final-outcome', (string) ($contract['laneId'] ?? 'heavy-governance')),
'decisionStatus' => (string) ($contract['decisionStatus'] ?? 'pending'),
'finalThresholdSeconds' => round((float) ($contract['normalizedThresholdSeconds'] ?? 0.0), 6),
'finalMeasuredSeconds' => $comparison['currentSeconds'],
'deltaSeconds' => $comparison['deltaSeconds'],
'deltaPercent' => $comparison['deltaPercent'],
'remainingOpenFamilies' => array_values($remainingOpenFamilies),
'justification' => $justification,
'followUpDebt' => $followUpDebt !== [] ? array_values($followUpDebt) : null,
], static fn (mixed $value): bool => $value !== null);
}
}

View File

@ -62,6 +62,13 @@ public static function manifest(): array
'budgetTargets' => self::budgetTargets(),
'lanes' => self::lanes(),
'familyBudgets' => self::familyBudgets(),
'heavyGovernanceBudgetContract' => self::heavyGovernanceBudgetContract(),
'heavyGovernanceHotspotInventory' => self::heavyGovernanceHotspotInventory(),
'heavyGovernanceDecompositionRecords' => self::heavyGovernanceDecompositionRecords(),
'heavyGovernanceSlimmingDecisions' => self::heavyGovernanceSlimmingDecisions(),
'heavyGovernanceBudgetSnapshots' => self::heavyGovernanceBudgetSnapshots(),
'heavyGovernanceBudgetOutcome' => self::heavyGovernanceBudgetOutcome(),
'heavyGovernanceAuthorGuidance' => self::heavyGovernanceAuthorGuidance(),
];
}
@ -1010,10 +1017,10 @@ public static function budgetTargets(): array
'budgetId' => 'lane-heavy-governance',
'targetType' => 'lane',
'targetId' => 'heavy-governance',
'thresholdSeconds' => 200,
'thresholdSeconds' => self::recommendedHeavyGovernanceNormalizedThreshold(),
'baselineSource' => 'measured-lane',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'lifecycleState' => self::heavyGovernanceBudgetContract()['lifecycleState'],
'reviewCadence' => 'refresh when heavy family ownership changes',
],
[
@ -1267,6 +1274,322 @@ public static function familyBudgets(): array
return $familyBudgets;
}
/**
* @return array<string, mixed>
*/
public static function heavyGovernanceBudgetContract(?float $measuredSeconds = null): array
{
$measuredSeconds ??= self::heavyGovernanceCurrentMeasuredSeconds();
$normalizedThresholdSeconds = self::recommendedHeavyGovernanceNormalizedThreshold($measuredSeconds);
$decisionStatus = $normalizedThresholdSeconds > self::heavyGovernanceSummaryThresholdSeconds()
? 'recalibrated'
: 'recovered';
return [
'laneId' => 'heavy-governance',
'summaryThresholdSeconds' => self::heavyGovernanceSummaryThresholdSeconds(),
'evaluationThresholdSeconds' => self::heavyGovernanceLegacyEvaluationThresholdSeconds(),
'normalizedThresholdSeconds' => $normalizedThresholdSeconds,
'baselineSource' => 'measured-lane',
'enforcementLevel' => 'warn',
'lifecycleState' => $decisionStatus === 'recalibrated' ? 'recalibrated' : 'documented',
'reconciliationRationale' => $decisionStatus === 'recalibrated'
? sprintf(
'Spec 209 removed duplicated workflow fan-out in the baseline-profile and findings-heavy families, but the settled lane still retains intentional surface-guard depth and the workspace settings residual workflow cost; the normalized contract is %.0fs after the honest rerun.',
$normalizedThresholdSeconds,
)
: 'Spec 209 removed enough duplicate workflow work for the heavy-governance lane to recover within the pre-normalization 300s contract.',
'decisionStatus' => $decisionStatus,
];
}
/**
* @return list<array<string, mixed>>
*/
public static function heavyGovernanceHotspotInventory(): array
{
$measuredSecondsByFamily = self::heavyGovernanceBaselineMeasuredSecondsByFamily();
$inventoryMetadata = [
'baseline-profile-start-surfaces' => ['costDriverCategory' => 'workflow-heavy', 'priorityTier' => 'primary', 'status' => 'slimmed'],
'action-surface-contract' => ['costDriverCategory' => 'intentionally-heavy', 'priorityTier' => 'primary', 'status' => 'retained'],
'ops-ux-governance' => ['costDriverCategory' => 'intentionally-heavy', 'priorityTier' => 'primary', 'status' => 'retained'],
'findings-workflow-surfaces' => ['costDriverCategory' => 'workflow-heavy', 'priorityTier' => 'primary', 'status' => 'slimmed'],
'finding-bulk-actions-workflow' => ['costDriverCategory' => 'redundant', 'priorityTier' => 'primary', 'status' => 'slimmed'],
'workspace-settings-slice-management' => ['costDriverCategory' => 'helper-driven', 'priorityTier' => 'primary', 'status' => 'follow-up'],
];
$inventory = [];
foreach ($inventoryMetadata as $familyId => $metadata) {
$family = self::family($familyId);
$budgetTarget = self::budgetTarget('family', $familyId);
$inventory[] = [
'familyId' => $familyId,
'classificationId' => $family['classificationId'],
'purpose' => $family['purpose'],
'measuredSeconds' => round((float) ($measuredSecondsByFamily[$familyId] ?? 0.0), 6),
'hotspotFiles' => $family['hotspotFiles'],
'costDriverCategory' => $metadata['costDriverCategory'],
'priorityTier' => $metadata['priorityTier'],
'currentBudgetSeconds' => isset($budgetTarget['thresholdSeconds']) ? (float) $budgetTarget['thresholdSeconds'] : null,
'status' => $metadata['status'],
];
}
usort($inventory, static fn (array $left, array $right): int => $right['measuredSeconds'] <=> $left['measuredSeconds']);
return $inventory;
}
/**
* @return list<array<string, mixed>>
*/
public static function heavyGovernanceDecompositionRecords(): array
{
return [
[
'familyId' => 'baseline-profile-start-surfaces',
'trustType' => 'workflow-trust',
'requiredBreadth' => 'One capture start surface, one compare start surface, and one authorization matrix on the baseline profile detail page.',
'duplicateWorkSources' => ['repeated-livewire-mounts', 'header-action-gating-matrix', 'duplicate-baseline-fixture-seeding'],
'duplicateWorkEstimateSeconds' => 12.0,
'residualCostSource' => 'family-breadth',
'recommendedAction' => 'centralize-work',
'notes' => 'The slimming pass keeps the capture and compare start-surface trust intact while reducing repeated Livewire page mounts and repeated start-surface gating setup.',
],
[
'familyId' => 'findings-workflow-surfaces',
'trustType' => 'workflow-trust',
'requiredBreadth' => 'Row actions, view-header actions, list-filter persistence, and renewal workflows remain separate trust slices but now share less repeated mounting work.',
'duplicateWorkSources' => ['repeated-livewire-mounts', 'filter-state-persistence', 'audit-fan-out'],
'duplicateWorkEstimateSeconds' => 6.0,
'residualCostSource' => 'family-breadth',
'recommendedAction' => 'centralize-work',
'notes' => 'The row and view workflow tests shed repeated page mounts while keeping state-transition, assignment, and audit guarantees explicit.',
],
[
'familyId' => 'finding-bulk-actions-workflow',
'trustType' => 'workflow-trust',
'requiredBreadth' => 'Bulk triage, assign, resolve, and close still need per-record workflow and audit verification, but the selected-record fan-out no longer needs a triple-digit fixture count.',
'duplicateWorkSources' => ['audit-fan-out', 'duplicate bulk-action setup'],
'duplicateWorkEstimateSeconds' => 20.0,
'residualCostSource' => 'family-breadth',
'recommendedAction' => 'narrow-assertions',
'notes' => 'The family now validates per-record audit fan-out with a representative selected set instead of an unnecessarily large fixture batch.',
],
[
'familyId' => 'action-surface-contract',
'trustType' => 'surface-trust',
'requiredBreadth' => 'The contract still needs broad action-surface discovery across resources, pages, and relation managers.',
'duplicateWorkSources' => ['resource-discovery-pass', 'surface-wide validation'],
'residualCostSource' => 'intentional-depth',
'recommendedAction' => 'retain-as-heavy',
'notes' => 'No repeatable duplication outweighed the intentionally broad governance surface, so the family remains heavy by design.',
],
[
'familyId' => 'ops-ux-governance',
'trustType' => 'surface-trust',
'requiredBreadth' => 'Ops UX still spans multiple monitoring, notification, and run-detail surfaces with legitimate governance depth.',
'duplicateWorkSources' => ['surface-wide validation', 'cross-surface workflow coverage'],
'residualCostSource' => 'intentional-depth',
'recommendedAction' => 'retain-as-heavy',
'notes' => 'The audit found real breadth rather than a removable duplicate pass, so the family is explicitly retained as intentional heavy coverage.',
],
[
'familyId' => 'workspace-settings-slice-management',
'trustType' => 'workflow-trust',
'requiredBreadth' => 'The settings surface still needs multi-slice save and reset verification, but its residual cost is mostly service and resolver fan-out rather than broad UI discovery.',
'duplicateWorkSources' => ['post-write resolver verification', 'multi-slice form workflow'],
'residualCostSource' => 'helper-driven',
'recommendedAction' => 'route-follow-up',
'notes' => 'Spec 209 records the residual settings cost explicitly instead of disguising it as a family-width win.',
],
];
}
/**
* @return list<array<string, mixed>>
*/
public static function heavyGovernanceSlimmingDecisions(): array
{
return [
[
'familyId' => 'baseline-profile-start-surfaces',
'decisionType' => 'centralize',
'scope' => [
'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php',
'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php',
'tests/Feature/Filament/BaselineActionAuthorizationTest.php',
],
'guardPreservationPlan' => 'Keep capture and compare launch authorization, rollout gating, and invalid-scope rejection on the real profile detail page while trimming repeated mount overhead.',
'expectedDeltaSeconds' => 12.0,
'owner' => 'platform-test-governance',
'validationPlan' => [
'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php',
'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php',
'tests/Feature/Filament/BaselineActionAuthorizationTest.php',
'scripts/platform-test-lane heavy-governance',
],
],
[
'familyId' => 'findings-workflow-surfaces',
'decisionType' => 'centralize',
'scope' => [
'tests/Feature/Findings/FindingWorkflowRowActionsTest.php',
'tests/Feature/Findings/FindingWorkflowViewActionsTest.php',
'tests/Feature/Findings/FindingsListFiltersTest.php',
'tests/Feature/Findings/FindingExceptionRenewalTest.php',
],
'guardPreservationPlan' => 'Preserve workflow-state, filter-persistence, and renewal evidence assertions while reducing repeated component bootstraps.',
'expectedDeltaSeconds' => 6.0,
'owner' => 'platform-test-governance',
'validationPlan' => [
'tests/Feature/Findings/FindingWorkflowRowActionsTest.php',
'tests/Feature/Findings/FindingWorkflowViewActionsTest.php',
'tests/Feature/Findings/FindingsListFiltersTest.php',
'tests/Feature/Findings/FindingExceptionRenewalTest.php',
],
],
[
'familyId' => 'finding-bulk-actions-workflow',
'decisionType' => 'trim-duplicate-work',
'scope' => ['tests/Feature/Findings/FindingBulkActionsTest.php'],
'guardPreservationPlan' => 'Keep per-record workflow transitions and audit assertions for each bulk action while cutting the oversized selected-record fan-out.',
'expectedDeltaSeconds' => 20.0,
'owner' => 'platform-test-governance',
'validationPlan' => [
'tests/Feature/Findings/FindingBulkActionsTest.php',
'scripts/platform-test-lane heavy-governance',
],
],
[
'familyId' => 'action-surface-contract',
'decisionType' => 'retain',
'guardPreservationPlan' => 'Retain the broad action-surface discovery contract until a future change proves a genuinely duplicate discovery pass.',
'owner' => 'platform-test-governance',
'validationPlan' => ['tests/Feature/Guards/ActionSurfaceContractTest.php'],
],
[
'familyId' => 'ops-ux-governance',
'decisionType' => 'retain',
'guardPreservationPlan' => 'Keep the broad Ops UX governance surface intact because the current cost comes from intentional coverage breadth, not an accidental duplicate loop.',
'owner' => 'platform-test-governance',
'validationPlan' => [
'tests/Feature/OpsUx/OperationCatalogCoverageTest.php',
'tests/Feature/OpsUx/OperateHubShellTest.php',
'tests/Feature/OpsUx/ActiveRunsTest.php',
],
],
[
'familyId' => 'workspace-settings-slice-management',
'decisionType' => 'follow-up',
'scope' => [
'tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php',
'tests/Support/TestLaneManifest.php',
],
'guardPreservationPlan' => 'Keep the current multi-slice save and reset assertions intact while recording the residual helper-driven cost for later follow-up.',
'owner' => 'platform-test-governance',
'validationPlan' => ['tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php'],
],
];
}
/**
* @return list<array<string, mixed>>
*/
public static function heavyGovernanceBudgetSnapshots(): array
{
return [
self::seededHeavyGovernanceBaselineSnapshot(),
self::currentHeavyGovernanceSnapshot(),
];
}
/**
* @return array<string, mixed>
*/
public static function heavyGovernanceBudgetOutcome(): array
{
$snapshots = self::heavyGovernanceBudgetSnapshots();
$contract = self::heavyGovernanceBudgetContract();
$remainingOpenFamilies = array_values(array_map(
static fn (array $record): string => $record['familyId'],
array_filter(
self::heavyGovernanceHotspotInventory(),
static fn (array $record): bool => in_array($record['status'], ['retained', 'follow-up'], true),
),
));
$followUpDebt = array_values(array_map(
static fn (array $decision): string => $decision['familyId'],
array_filter(
self::heavyGovernanceSlimmingDecisions(),
static fn (array $decision): bool => in_array($decision['decisionType'], ['retain', 'follow-up'], true),
),
));
$justification = $contract['decisionStatus'] === 'recalibrated'
? sprintf(
'The workflow-heavy hotspot families were narrowed, but the honest lane still retains intentional surface-guard depth and the workspace settings residual helper cost, so the normalized heavy-governance threshold is %.0fs.',
$contract['normalizedThresholdSeconds'],
)
: 'The baseline-profile and findings-heavy families recovered enough duplicated workflow cost for the heavy-governance lane to fit inside the authoritative 300s threshold.';
return TestLaneBudget::buildOutcomeRecord(
contract: $contract,
baselineSnapshot: $snapshots[0],
currentSnapshot: $snapshots[1],
remainingOpenFamilies: $remainingOpenFamilies,
justification: $justification,
followUpDebt: $followUpDebt,
);
}
/**
* @return list<array<string, mixed>>
*/
public static function heavyGovernanceAuthorGuidance(): array
{
return [
[
'ruleId' => 'heavy-family-reuse-before-creation',
'whenToUse' => 'A new heavy-governance test touches a known baseline, findings, settings, or surface-guard family.',
'requiredDecision' => 'Decide whether the new test belongs in the existing family before creating a new family id.',
'antiPattern' => 'Creating a new heavy family for a scenario that only extends an existing trust boundary.',
'preferredOutcome' => 'Reuse the existing family and update its hotspot or decomposition notes when the trust type stays the same.',
],
[
'ruleId' => 'heavy-family-create-only-for-new-trust',
'whenToUse' => 'A proposed heavy test introduces a trust boundary that is not already represented in the canonical family inventory.',
'requiredDecision' => 'Explain the new trust type, hotspot files, and lane rationale before adding the family.',
'antiPattern' => 'Adding a vague catch-all family without a stable trust description and hotspot inventory entry.',
'preferredOutcome' => 'Create a new family only when the trust boundary and hotspot ownership are both new and reviewable.',
],
[
'ruleId' => 'split-discovery-workflow-surface-concerns',
'whenToUse' => 'A test mixes discovery, workflow, and surface-discipline assertions inside one heavy file.',
'requiredDecision' => 'Separate discovery-trust, workflow-trust, and surface-trust when the same setup is proving unrelated governance rules.',
'antiPattern' => 'One heavy test that silently becomes a catch-all for unrelated trust types.',
'preferredOutcome' => 'Keep each heavy family anchored to one dominant trust type, with any unavoidable secondary cost called out explicitly.',
],
[
'ruleId' => 'retain-intentional-heavy-depth-explicitly',
'whenToUse' => 'A heavy family stays expensive after duplicate work has been removed.',
'requiredDecision' => 'Record that the family is intentionally heavy and explain the remaining governance breadth.',
'antiPattern' => 'Continuing to treat intentional heavy coverage as unexplained budget drift.',
'preferredOutcome' => 'Mark the family as retained with an intentional-depth rationale and validate it with a focused guard suite.',
],
[
'ruleId' => 'record-helper-or-fixture-residuals',
'whenToUse' => 'A hotspot is dominated by helper, resolver, or fixture cost rather than broad UI trust.',
'requiredDecision' => 'Record the residual helper or fixture cause instead of pretending the family itself was fully slimmed.',
'antiPattern' => 'Claiming a family-width win when the remaining cost is actually outside the family boundary.',
'preferredOutcome' => 'Route the residual helper or fixture cost into follow-up debt while keeping the family inventory honest.',
],
];
}
/**
* @return array<string, mixed>|null
*/
@ -1464,10 +1787,10 @@ public static function lanes(): array
],
'artifacts' => ['summary', 'junit-xml', 'budget-report'],
'budget' => [
'thresholdSeconds' => 300,
'thresholdSeconds' => self::recommendedHeavyGovernanceNormalizedThreshold(),
'baselineSource' => 'measured-lane',
'enforcement' => 'warn',
'lifecycleState' => 'documented',
'lifecycleState' => self::heavyGovernanceBudgetContract()['lifecycleState'],
'reviewCadence' => 'tighten after two stable runs',
],
'dbStrategy' => [
@ -1897,6 +2220,179 @@ public static function describeFilePlacement(string $filePath): string
return $message;
}
private static function heavyGovernanceSummaryThresholdSeconds(): float
{
return 300.0;
}
private static function heavyGovernanceLegacyEvaluationThresholdSeconds(): float
{
return 200.0;
}
private static function recommendedHeavyGovernanceNormalizedThreshold(?float $measuredSeconds = null): float
{
$resolvedMeasuredSeconds = round($measuredSeconds ?? self::heavyGovernanceCurrentMeasuredSeconds(), 6);
if ($resolvedMeasuredSeconds <= self::heavyGovernanceSummaryThresholdSeconds()) {
return self::heavyGovernanceSummaryThresholdSeconds();
}
return round((float) (ceil($resolvedMeasuredSeconds / 5.0) * 5.0), 6);
}
private static function heavyGovernanceCurrentMeasuredSeconds(): float
{
$report = self::readJsonArtifact(self::heavyGovernanceArtifactPaths('latest')['report']);
if (is_array($report)) {
return round((float) ($report['wallClockSeconds'] ?? 0.0), 6);
}
return round((float) self::seededHeavyGovernanceBaselineSnapshot()['wallClockSeconds'], 6);
}
/**
* @return array{summary: string, budget: string, report: string}
*/
private static function heavyGovernanceArtifactPaths(string $variant): array
{
$directory = self::artifactDirectory();
$suffix = $variant === 'baseline' ? 'baseline' : 'latest';
return [
'summary' => sprintf('%s/heavy-governance-%s.summary.md', $directory, $suffix),
'budget' => sprintf('%s/heavy-governance-%s.budget.json', $directory, $suffix),
'report' => sprintf('%s/heavy-governance-%s.report.json', $directory, $suffix),
];
}
/**
* @return array<string, mixed>|null
*/
private static function readJsonArtifact(string $relativePath): ?array
{
$absolutePath = self::absolutePath($relativePath);
if (! is_file($absolutePath)) {
return null;
}
$decoded = json_decode((string) file_get_contents($absolutePath), true);
return is_array($decoded) ? $decoded : null;
}
/**
* @return array<string, float>
*/
private static function heavyGovernanceBaselineMeasuredSecondsByFamily(): array
{
$snapshot = self::seededHeavyGovernanceBaselineSnapshot();
$totals = [];
foreach ($snapshot['familyTotals'] as $entry) {
$totals[(string) $entry['familyId']] = round((float) $entry['totalWallClockSeconds'], 6);
}
return $totals;
}
/**
* @return array<string, mixed>
*/
private static function currentHeavyGovernanceSnapshot(): array
{
$fallback = self::seededHeavyGovernanceBaselineSnapshot();
$report = self::readJsonArtifact(self::heavyGovernanceArtifactPaths('latest')['report']);
$measuredSeconds = round((float) ($report['wallClockSeconds'] ?? $fallback['wallClockSeconds']), 6);
$normalizedThresholdSeconds = self::recommendedHeavyGovernanceNormalizedThreshold($measuredSeconds);
if (! is_array($report)) {
return array_merge($fallback, [
'snapshotId' => 'post-slimming',
'artifactPaths' => self::heavyGovernanceArtifactPaths('latest'),
'budgetStatus' => $measuredSeconds <= $normalizedThresholdSeconds ? 'within-budget' : 'warning',
]);
}
return [
'snapshotId' => 'post-slimming',
'capturedAt' => (string) ($report['finishedAt'] ?? gmdate('c')),
'wallClockSeconds' => $measuredSeconds,
'classificationTotals' => array_map(
static fn (array $entry): array => [
'classificationId' => (string) $entry['classificationId'],
'totalWallClockSeconds' => round((float) $entry['totalWallClockSeconds'], 6),
],
$report['classificationAttribution'] ?? [],
),
'familyTotals' => array_map(
static fn (array $entry): array => [
'familyId' => (string) $entry['familyId'],
'totalWallClockSeconds' => round((float) $entry['totalWallClockSeconds'], 6),
],
$report['familyAttribution'] ?? [],
),
'slowestEntries' => array_map(
static fn (array $entry): array => [
'label' => (string) $entry['label'],
'wallClockSeconds' => round((float) ($entry['wallClockSeconds'] ?? 0.0), 6),
],
$report['slowestEntries'] ?? [],
),
'artifactPaths' => self::heavyGovernanceArtifactPaths('latest'),
'budgetStatus' => $measuredSeconds <= $normalizedThresholdSeconds ? 'within-budget' : 'warning',
];
}
/**
* @return array<string, mixed>
*/
private static function seededHeavyGovernanceBaselineSnapshot(): array
{
return [
'snapshotId' => 'pre-slimming',
'capturedAt' => '2026-04-17T11:00:53+00:00',
'wallClockSeconds' => 318.296962,
'classificationTotals' => [
['classificationId' => 'ui-workflow', 'totalWallClockSeconds' => 190.606431],
['classificationId' => 'surface-guard', 'totalWallClockSeconds' => 106.845887],
['classificationId' => 'discovery-heavy', 'totalWallClockSeconds' => 0.863003],
],
'familyTotals' => [
['familyId' => 'baseline-profile-start-surfaces', 'totalWallClockSeconds' => 98.112193],
['familyId' => 'action-surface-contract', 'totalWallClockSeconds' => 40.841552],
['familyId' => 'ops-ux-governance', 'totalWallClockSeconds' => 38.794861],
['familyId' => 'findings-workflow-surfaces', 'totalWallClockSeconds' => 36.459493],
['familyId' => 'finding-bulk-actions-workflow', 'totalWallClockSeconds' => 26.491446],
['familyId' => 'workspace-settings-slice-management', 'totalWallClockSeconds' => 21.740839],
['familyId' => 'workspace-only-admin-surface-independence', 'totalWallClockSeconds' => 11.639077],
['familyId' => 'panel-navigation-segregation', 'totalWallClockSeconds' => 11.022529],
['familyId' => 'drift-bulk-triage-all-matching', 'totalWallClockSeconds' => 7.80246],
['familyId' => 'backup-items-relation-manager-enforcement', 'totalWallClockSeconds' => 2.280078],
['familyId' => 'tenant-review-header-discipline', 'totalWallClockSeconds' => 1.257656],
['familyId' => 'workspace-memberships-relation-manager-enforcement', 'totalWallClockSeconds' => 1.010134],
['familyId' => 'policy-resource-admin-search-parity', 'totalWallClockSeconds' => 0.439257],
['familyId' => 'policy-version-admin-search-parity', 'totalWallClockSeconds' => 0.423746],
],
'slowestEntries' => [
['label' => 'tests/Feature/Findings/FindingBulkActionsTest.php::it supports bulk workflow actions and audits each record', 'wallClockSeconds' => 26.491446],
['label' => 'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php::it starts capture successfully for authorized workspace members', 'wallClockSeconds' => 12.373413],
['label' => 'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php::it starts baseline compare successfully for authorized workspace members', 'wallClockSeconds' => 12.228384],
['label' => 'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php::it does not start full-content baseline compare when rollout is disabled', 'wallClockSeconds' => 11.204111],
['label' => 'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php::it does not start full-content capture when rollout is disabled', 'wallClockSeconds' => 11.086623],
['label' => 'tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php::it does not start baseline compare for workspace members missing tenant.sync', 'wallClockSeconds' => 10.659623],
['label' => 'tests/Feature/Filament/BaselineActionAuthorizationTest.php::it keeps baseline capture and compare actions capability-gated on the profile detail page', 'wallClockSeconds' => 10.555709],
['label' => 'tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php::it does not start capture for workspace members missing workspace_baselines.manage', 'wallClockSeconds' => 10.428982],
['label' => 'tests/Feature/Drift/DriftBulkAcknowledgeAllMatchingConfirmationTest.php::triage all matching requires typed confirmation when triaging more than 100 findings', 'wallClockSeconds' => 7.80246],
['label' => 'tests/Feature/Filament/WorkspaceOnlySurfaceTenantIndependenceTest.php::it keeps workspace-only admin surfaces independent from remembered tenant changes', 'wallClockSeconds' => 7.779388],
],
'artifactPaths' => self::heavyGovernanceArtifactPaths('baseline'),
'budgetStatus' => 'warning',
];
}
private static function appRoot(): string
{
return dirname(__DIR__, 2);

View File

@ -91,6 +91,15 @@ public static function buildReport(
?string $comparisonProfile = null,
): array {
$lane = TestLaneManifest::lane($laneId);
$heavyGovernanceContract = $laneId === 'heavy-governance'
? TestLaneManifest::heavyGovernanceBudgetContract($wallClockSeconds)
: null;
if (is_array($heavyGovernanceContract)) {
$lane['budget']['thresholdSeconds'] = $heavyGovernanceContract['normalizedThresholdSeconds'];
$lane['budget']['lifecycleState'] = $heavyGovernanceContract['lifecycleState'];
}
$laneBudget = TestLaneBudget::fromArray($lane['budget']);
$laneBudgetEvaluation = $laneBudget->evaluate($wallClockSeconds);
$artifactPaths = self::artifactPaths($laneId, $artifactDirectory);
@ -117,12 +126,30 @@ public static function buildReport(
}
$attribution = self::buildAttribution($durationsByFile);
$relevantBudgetTargets = self::relevantBudgetTargets(
$laneId,
$attribution['classificationTotals'],
$attribution['familyTotals'],
);
if (is_array($heavyGovernanceContract)) {
$relevantBudgetTargets = array_values(array_map(
static function (array $budgetTarget) use ($heavyGovernanceContract): array {
if (($budgetTarget['targetType'] ?? null) !== 'lane' || ($budgetTarget['targetId'] ?? null) !== 'heavy-governance') {
return $budgetTarget;
}
$budgetTarget['thresholdSeconds'] = $heavyGovernanceContract['normalizedThresholdSeconds'];
$budgetTarget['lifecycleState'] = $heavyGovernanceContract['lifecycleState'];
return $budgetTarget;
},
$relevantBudgetTargets,
));
}
$budgetEvaluations = TestLaneBudget::evaluateBudgetTargets(
self::relevantBudgetTargets(
$laneId,
$attribution['classificationTotals'],
$attribution['familyTotals'],
),
$relevantBudgetTargets,
$wallClockSeconds,
$attribution['classificationTotals'],
$attribution['familyTotals'],
@ -142,6 +169,21 @@ static function (array $entry) use ($attribution): array {
$slowestEntries,
));
$heavyGovernanceContext = $laneId === 'heavy-governance'
? self::buildHeavyGovernanceContext(
budgetContract: $heavyGovernanceContract ?? TestLaneManifest::heavyGovernanceBudgetContract(),
wallClockSeconds: $wallClockSeconds,
artifactPaths: [
'summary' => $artifactPaths['summary'],
'budget' => $artifactPaths['budget'],
'report' => $artifactPaths['report'],
],
classificationAttribution: $attribution['classificationAttribution'],
familyAttribution: $attribution['familyAttribution'],
slowestEntries: $enrichedSlowestEntries,
)
: [];
$report = [
'laneId' => $laneId,
'artifactDirectory' => trim($artifactDirectory ?? TestLaneManifest::artifactDirectory(), '/'),
@ -163,6 +205,10 @@ static function (array $entry) use ($attribution): array {
'artifacts' => $artifacts,
];
if ($heavyGovernanceContext !== []) {
$report = array_merge($report, $heavyGovernanceContext);
}
if ($laneBudget->baselineDeltaTargetPercent !== null) {
$report['baselineDeltaTargetPercent'] = $laneBudget->baselineDeltaTargetPercent;
}
@ -210,6 +256,12 @@ public static function writeArtifacts(
'familyAttribution' => $report['familyAttribution'],
'budgetEvaluations' => $report['budgetEvaluations'],
'familyBudgetEvaluations' => $report['familyBudgetEvaluations'],
'budgetContract' => $report['budgetContract'] ?? null,
'inventoryCoverage' => $report['inventoryCoverage'] ?? null,
'budgetSnapshots' => $report['budgetSnapshots'] ?? null,
'budgetOutcome' => $report['budgetOutcome'] ?? null,
'remainingOpenFamilies' => $report['remainingOpenFamilies'] ?? null,
'stabilizedFamilies' => $report['stabilizedFamilies'] ?? null,
'sharedFixtureSlimmingComparison' => $report['sharedFixtureSlimmingComparison'] ?? null,
], JSON_PRETTY_PRINT | JSON_THROW_ON_ERROR),
);
@ -264,6 +316,35 @@ private static function buildSummaryMarkdown(array $report): string
sprintf('- Budget: %d seconds (%s)', (int) $report['budgetThresholdSeconds'], $report['budgetStatus']),
];
if (($report['laneId'] ?? null) === 'heavy-governance' && isset($report['budgetContract']) && is_array($report['budgetContract'])) {
$lines[] = sprintf(
'- Budget contract: %.0f seconds (%s)',
(float) $report['budgetContract']['normalizedThresholdSeconds'],
(string) ($report['budgetOutcome']['decisionStatus'] ?? $report['budgetContract']['decisionStatus'] ?? 'pending'),
);
$lines[] = sprintf(
'- Legacy drift signal: %.0f seconds (pre-normalization evidence)',
(float) $report['budgetContract']['evaluationThresholdSeconds'],
);
if (isset($report['budgetOutcome']['deltaSeconds'], $report['budgetOutcome']['deltaPercent'])) {
$lines[] = sprintf(
'- Baseline delta: %+0.2f seconds (%+0.2f%%)',
(float) $report['budgetOutcome']['deltaSeconds'],
(float) $report['budgetOutcome']['deltaPercent'],
);
}
if (isset($report['inventoryCoverage']) && is_array($report['inventoryCoverage'])) {
$lines[] = sprintf(
'- Inventory coverage: %.2f%% across %d required families (%s)',
(float) $report['inventoryCoverage']['coveredRuntimePercent'],
(int) $report['inventoryCoverage']['requiredFamilyCount'],
(bool) $report['inventoryCoverage']['meetsInclusionRule'] ? 'meets inclusion rule' : 'missing required families',
);
}
}
if (isset($report['sharedFixtureSlimmingComparison']) && is_array($report['sharedFixtureSlimmingComparison'])) {
$comparison = $report['sharedFixtureSlimmingComparison'];
@ -447,6 +528,153 @@ private static function buildSharedFixtureSlimmingComparison(
];
}
/**
* @param list<array<string, mixed>> $classificationAttribution
* @param list<array<string, mixed>> $familyAttribution
* @param list<array<string, mixed>> $slowestEntries
* @param array{summary: string, budget: string, report: string} $artifactPaths
* @param array<string, mixed> $budgetContract
* @return array<string, mixed>
*/
private static function buildHeavyGovernanceContext(
array $budgetContract,
float $wallClockSeconds,
array $artifactPaths,
array $classificationAttribution,
array $familyAttribution,
array $slowestEntries,
): array {
$inventory = TestLaneManifest::heavyGovernanceHotspotInventory();
$inventoryCoverage = self::buildHeavyGovernanceInventoryCoverage($familyAttribution, $inventory, $wallClockSeconds);
$budgetSnapshots = [
TestLaneManifest::heavyGovernanceBudgetSnapshots()[0],
[
'snapshotId' => 'post-slimming',
'capturedAt' => gmdate('c'),
'wallClockSeconds' => round($wallClockSeconds, 6),
'classificationTotals' => array_map(
static fn (array $entry): array => [
'classificationId' => (string) $entry['classificationId'],
'totalWallClockSeconds' => round((float) $entry['totalWallClockSeconds'], 6),
],
$classificationAttribution,
),
'familyTotals' => array_map(
static fn (array $entry): array => [
'familyId' => (string) $entry['familyId'],
'totalWallClockSeconds' => round((float) $entry['totalWallClockSeconds'], 6),
],
$familyAttribution,
),
'slowestEntries' => array_map(
static fn (array $entry): array => [
'label' => (string) $entry['label'],
'wallClockSeconds' => round((float) ($entry['wallClockSeconds'] ?? 0.0), 6),
],
$slowestEntries,
),
'artifactPaths' => $artifactPaths,
'budgetStatus' => (string) TestLaneBudget::evaluateGovernanceContract($budgetContract, $wallClockSeconds)['budgetStatus'],
],
];
$remainingOpenFamilies = array_values(array_map(
static fn (array $record): string => $record['familyId'],
array_filter(
$inventory,
static fn (array $record): bool => in_array($record['status'], ['retained', 'follow-up'], true),
),
));
$stabilizedFamilies = array_values(array_map(
static fn (array $record): string => $record['familyId'],
array_filter(
$inventory,
static fn (array $record): bool => $record['status'] === 'slimmed',
),
));
$followUpDebt = array_values(array_map(
static fn (array $decision): string => $decision['familyId'],
array_filter(
TestLaneManifest::heavyGovernanceSlimmingDecisions(),
static fn (array $decision): bool => in_array($decision['decisionType'], ['retain', 'follow-up'], true),
),
));
$budgetOutcome = TestLaneBudget::buildOutcomeRecord(
contract: $budgetContract,
baselineSnapshot: $budgetSnapshots[0],
currentSnapshot: $budgetSnapshots[1],
remainingOpenFamilies: $remainingOpenFamilies,
justification: $budgetContract['decisionStatus'] === 'recalibrated'
? sprintf(
'The primary workflow-heavy hotspots were slimmed, but the lane still retains intentional surface-guard depth and the workspace settings residual helper cost, so the authoritative threshold is now %.0fs.',
$budgetContract['normalizedThresholdSeconds'],
)
: 'The primary workflow-heavy hotspots slimmed enough duplicated work for the heavy-governance lane to recover within 300 seconds.',
followUpDebt: $followUpDebt,
);
return [
'budgetContract' => $budgetContract,
'hotspotInventory' => $inventory,
'decompositionRecords' => TestLaneManifest::heavyGovernanceDecompositionRecords(),
'slimmingDecisions' => TestLaneManifest::heavyGovernanceSlimmingDecisions(),
'authorGuidance' => TestLaneManifest::heavyGovernanceAuthorGuidance(),
'inventoryCoverage' => $inventoryCoverage,
'budgetSnapshots' => $budgetSnapshots,
'budgetOutcome' => $budgetOutcome,
'remainingOpenFamilies' => $remainingOpenFamilies,
'stabilizedFamilies' => $stabilizedFamilies,
];
}
/**
* @param list<array<string, mixed>> $familyAttribution
* @param list<array<string, mixed>> $inventory
* @return array<string, mixed>
*/
private static function buildHeavyGovernanceInventoryCoverage(array $familyAttribution, array $inventory, float $laneSeconds): array
{
$requiredFamilyIds = [];
$coveredSeconds = 0.0;
foreach ($familyAttribution as $entry) {
$requiredFamilyIds[] = (string) $entry['familyId'];
$coveredSeconds += (float) ($entry['totalWallClockSeconds'] ?? 0.0);
if (count($requiredFamilyIds) >= 5 && ($laneSeconds <= 0.0 || ($coveredSeconds / $laneSeconds) >= 0.8)) {
break;
}
}
$inventoryFamilyIds = array_values(array_map(static fn (array $entry): string => $entry['familyId'], $inventory));
$coveredFamilyIds = array_values(array_intersect($requiredFamilyIds, $inventoryFamilyIds));
$coveredRuntimeSeconds = array_reduce(
$familyAttribution,
static function (float $carry, array $entry) use ($coveredFamilyIds): float {
if (! in_array((string) $entry['familyId'], $coveredFamilyIds, true)) {
return $carry;
}
return $carry + (float) ($entry['totalWallClockSeconds'] ?? 0.0);
},
0.0,
);
$coveredRuntimePercent = $laneSeconds > 0.0
? round(($coveredRuntimeSeconds / $laneSeconds) * 100, 6)
: 0.0;
return [
'requiredFamilyCount' => count($requiredFamilyIds),
'requiredFamilyIds' => $requiredFamilyIds,
'inventoryFamilyCount' => count($inventoryFamilyIds),
'inventoryFamilyIds' => $inventoryFamilyIds,
'coveredFamilyIds' => $coveredFamilyIds,
'coveredRuntimeSeconds' => round($coveredRuntimeSeconds, 6),
'coveredRuntimePercent' => $coveredRuntimePercent,
'meetsInclusionRule' => count($coveredFamilyIds) === count($requiredFamilyIds),
'topHotspots' => array_slice($familyAttribution, 0, 10),
];
}
private static function ensureDirectory(string $directory): void
{
if (is_dir($directory)) {

View File

@ -6,6 +6,21 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
APP_DIR="${ROOT_DIR}/apps/platform"
LANE="${1:-fast-feedback}"
CAPTURE_BASELINE=false
copy_heavy_baseline_artifacts() {
local artifact_root="${APP_DIR}/storage/logs/test-lanes"
local suffix
for suffix in junit.xml summary.md budget.json report.json profile.txt; do
local latest_path="${artifact_root}/heavy-governance-latest.${suffix}"
local baseline_path="${artifact_root}/heavy-governance-baseline.${suffix}"
if [[ -f "${latest_path}" ]]; then
cp "${latest_path}" "${baseline_path}"
fi
done
}
case "${LANE}" in
fast-feedback|fast|default)
@ -34,6 +49,30 @@ esac
shift || true
remaining_args=()
for arg in "$@"; do
if [[ "${arg}" == "--capture-baseline" ]]; then
CAPTURE_BASELINE=true
continue
fi
remaining_args+=("${arg}")
done
if [[ "${CAPTURE_BASELINE}" == true && "${LANE}" != "heavy-governance" && "${LANE}" != "heavy" ]]; then
echo "--capture-baseline is only supported for heavy-governance" >&2
exit 1
fi
cd "${APP_DIR}"
exec ./vendor/bin/sail composer run --timeout=0 "${COMPOSER_SCRIPT}" -- "$@"
if [[ ${#remaining_args[@]} -gt 0 ]]; then
./vendor/bin/sail composer run --timeout=0 "${COMPOSER_SCRIPT}" -- "${remaining_args[@]}"
else
./vendor/bin/sail composer run --timeout=0 "${COMPOSER_SCRIPT}"
fi
if [[ "${CAPTURE_BASELINE}" == true ]]; then
copy_heavy_baseline_artifacts
fi

View File

@ -6,6 +6,21 @@ SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
APP_DIR="${ROOT_DIR}/apps/platform"
LANE="${1:-fast-feedback}"
CAPTURE_BASELINE=false
copy_heavy_baseline_artifacts() {
local artifact_root="${APP_DIR}/storage/logs/test-lanes"
local suffix
for suffix in summary.md budget.json report.json; do
local latest_path="${artifact_root}/heavy-governance-latest.${suffix}"
local baseline_path="${artifact_root}/heavy-governance-baseline.${suffix}"
if [[ -f "${latest_path}" ]]; then
cp "${latest_path}" "${baseline_path}"
fi
done
}
case "${LANE}" in
fast-feedback|fast|default)
@ -29,6 +44,27 @@ case "${LANE}" in
;;
esac
shift || true
for arg in "$@"; do
if [[ "${arg}" == "--capture-baseline" ]]; then
CAPTURE_BASELINE=true
continue
fi
echo "Unknown option: ${arg}" >&2
exit 1
done
if [[ "${CAPTURE_BASELINE}" == true && "${LANE}" != "heavy-governance" && "${LANE}" != "heavy" ]]; then
echo "--capture-baseline is only supported for heavy-governance" >&2
exit 1
fi
cd "${APP_DIR}"
exec ./vendor/bin/sail composer run --timeout=0 "${COMPOSER_SCRIPT}"
./vendor/bin/sail composer run --timeout=0 "${COMPOSER_SCRIPT}"
if [[ "${CAPTURE_BASELINE}" == true ]]; then
copy_heavy_baseline_artifacts
fi

View File

@ -0,0 +1,38 @@
# Specification Quality Checklist: Heavy Governance Lane Cost Reduction
**Purpose**: Validate specification completeness and quality before proceeding to planning
**Created**: 2026-04-17
**Feature**: [spec.md](../spec.md)
## Content Quality
- [x] No implementation details (languages, frameworks, APIs)
- [x] Focused on user value and business needs
- [x] Written for non-technical stakeholders
- [x] All mandatory sections completed
## Requirement Completeness
- [x] No [NEEDS CLARIFICATION] markers remain
- [x] Requirements are testable and unambiguous
- [x] Success criteria are measurable
- [x] Success criteria are technology-agnostic (no implementation details)
- [x] All acceptance scenarios are defined
- [x] Edge cases are identified
- [x] Scope is clearly bounded
- [x] Dependencies and assumptions identified
## Feature Readiness
- [x] All functional requirements have clear acceptance criteria
- [x] User scenarios cover primary flows
- [x] Feature meets measurable outcomes defined in Success Criteria
- [x] No implementation details leak into specification
## Notes
- Validation run: 2026-04-17
- No template placeholders or [NEEDS CLARIFICATION] markers remain.
- The spec stays repo-governance-focused: it describes lane behavior, hotspot evidence, and budget outcomes without prescribing language-, framework-, or API-level implementation.
- The spec keeps Heavy Governance honest by requiring explicit budget recovery or explicit recalibration, rather than silent lane reshuffling.
- Items marked incomplete require spec updates before `/speckit.clarify` or `/speckit.plan`

View File

@ -0,0 +1,509 @@
openapi: 3.1.0
info:
title: Heavy Governance Cost Recovery Logical Contract
version: 1.0.0
summary: Logical contract for inspecting heavy-governance hotspots, recording slimming decisions, and publishing the final budget outcome.
description: |
This is a logical contract for repository tooling, tests, and planning artifacts.
It does not imply a new runtime HTTP service. It documents the expected
semantics of hotspot inventory, family decomposition, budget normalization,
and explicit recovery or recalibration outcomes so the existing heavy-lane
seams remain consistent as Spec 209 is implemented.
x-logical-contract: true
servers:
- url: https://tenantatlas.local/logical
paths:
/heavy-governance/budget-contract:
get:
summary: Read the current heavy-governance budget contract.
operationId: getHeavyGovernanceBudgetContract
responses:
'200':
description: Current heavy-governance budget contract.
content:
application/json:
schema:
$ref: '#/components/schemas/HeavyGovernanceBudgetContract'
/heavy-governance/hotspots:
get:
summary: Read the current heavy-governance hotspot inventory.
operationId: listHeavyGovernanceHotspots
responses:
'200':
description: Current hotspot inventory.
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- hotspots
properties:
hotspots:
type: array
minItems: 1
items:
$ref: '#/components/schemas/HeavyGovernanceHotspot'
/heavy-governance/hotspots/{familyId}/decomposition:
get:
summary: Read the decomposition record for a hotspot family.
operationId: getHeavyGovernanceFamilyDecomposition
parameters:
- name: familyId
in: path
required: true
schema:
type: string
responses:
'200':
description: Current decomposition record for the family.
content:
application/json:
schema:
$ref: '#/components/schemas/FamilyCostDecomposition'
put:
summary: Record or update the decomposition record for a hotspot family.
operationId: putHeavyGovernanceFamilyDecomposition
parameters:
- name: familyId
in: path
required: true
schema:
type: string
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/FamilyCostDecomposition'
responses:
'200':
description: Decomposition record stored logically.
content:
application/json:
schema:
$ref: '#/components/schemas/FamilyCostDecomposition'
/heavy-governance/slimming-decisions:
get:
summary: Read the current slimming decisions for targeted hotspot families.
operationId: listHeavyGovernanceSlimmingDecisions
responses:
'200':
description: Current slimming decisions.
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- decisions
properties:
decisions:
type: array
items:
$ref: '#/components/schemas/HeavyFamilySlimmingDecision'
/heavy-governance/budget-snapshots:
get:
summary: Read the recorded heavy-governance budget snapshots.
operationId: listHeavyGovernanceBudgetSnapshots
responses:
'200':
description: Current before-and-after snapshots.
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- snapshots
properties:
snapshots:
type: array
items:
$ref: '#/components/schemas/BudgetRecoverySnapshot'
/heavy-governance/budget-outcome/latest:
get:
summary: Read the latest explicit heavy-governance budget outcome.
operationId: getLatestHeavyGovernanceBudgetOutcome
responses:
'200':
description: Latest heavy-governance budget outcome.
content:
application/json:
schema:
$ref: '#/components/schemas/BudgetOutcomeRecord'
/heavy-governance/author-guidance:
get:
summary: Read the reviewer and author rules for future heavy-governance tests.
operationId: listHeavyGovernanceAuthorGuidance
responses:
'200':
description: Current author guidance rules.
content:
application/json:
schema:
type: object
additionalProperties: false
required:
- rules
properties:
rules:
type: array
minItems: 4
items:
$ref: '#/components/schemas/HeavyAuthorGuidanceRule'
components:
schemas:
ClassificationId:
type: string
enum:
- ui-workflow
- surface-guard
- discovery-heavy
CostDriverCategory:
type: string
enum:
- overbroad
- redundant
- discovery-heavy
- workflow-heavy
- surface-heavy
- helper-driven
- fixture-driven
- intentionally-heavy
BudgetDecisionStatus:
type: string
enum:
- pending
- recovered
- recalibrated
HotspotStatus:
type: string
enum:
- seeded
- decomposed
- slimmed
- retained
- follow-up
PriorityTier:
type: string
enum:
- primary
- secondary
- residual
ResidualCostSource:
type: string
enum:
- family-breadth
- helper-driven
- fixture-driven
- mixed
- intentional-depth
RecommendedAction:
type: string
enum:
- split-family
- centralize-work
- narrow-assertions
- retain-as-heavy
- route-follow-up
SlimmingDecisionType:
type: string
enum:
- split
- centralize
- trim-duplicate-work
- retain
- follow-up
BudgetStatus:
type: string
enum:
- within-budget
- warning
- over-budget
HeavyGovernanceBudgetContract:
type: object
additionalProperties: false
required:
- laneId
- summaryThresholdSeconds
- evaluationThresholdSeconds
- normalizedThresholdSeconds
- baselineSource
- enforcementLevel
- lifecycleState
- decisionStatus
properties:
laneId:
type: string
const: heavy-governance
summaryThresholdSeconds:
type: number
exclusiveMinimum: 0
evaluationThresholdSeconds:
type: number
exclusiveMinimum: 0
normalizedThresholdSeconds:
type: number
exclusiveMinimum: 0
baselineSource:
type: string
enforcementLevel:
type: string
enum:
- report-only
- warn
- hard-fail
lifecycleState:
type: string
enum:
- draft
- documented
- recalibrated
reconciliationRationale:
type: string
decisionStatus:
$ref: '#/components/schemas/BudgetDecisionStatus'
HeavyGovernanceHotspot:
type: object
additionalProperties: false
required:
- familyId
- classificationId
- purpose
- measuredSeconds
- hotspotFiles
- costDriverCategory
- priorityTier
- status
properties:
familyId:
type: string
classificationId:
$ref: '#/components/schemas/ClassificationId'
purpose:
type: string
measuredSeconds:
type: number
minimum: 0
hotspotFiles:
type: array
minItems: 1
items:
type: string
costDriverCategory:
$ref: '#/components/schemas/CostDriverCategory'
priorityTier:
$ref: '#/components/schemas/PriorityTier'
currentBudgetSeconds:
type: number
minimum: 0
status:
$ref: '#/components/schemas/HotspotStatus'
FamilyCostDecomposition:
type: object
additionalProperties: false
required:
- familyId
- trustType
- requiredBreadth
- duplicateWorkSources
- residualCostSource
- recommendedAction
- notes
properties:
familyId:
type: string
trustType:
type: string
enum:
- workflow-trust
- surface-trust
- guard-trust
- discovery-trust
requiredBreadth:
type: string
duplicateWorkSources:
type: array
items:
type: string
duplicateWorkEstimateSeconds:
type: number
minimum: 0
residualCostSource:
$ref: '#/components/schemas/ResidualCostSource'
recommendedAction:
$ref: '#/components/schemas/RecommendedAction'
notes:
type: string
HeavyFamilySlimmingDecision:
type: object
additionalProperties: false
required:
- familyId
- decisionType
- guardPreservationPlan
- owner
- validationPlan
properties:
familyId:
type: string
decisionType:
$ref: '#/components/schemas/SlimmingDecisionType'
scope:
type: array
items:
type: string
guardPreservationPlan:
type: string
expectedDeltaSeconds:
type: number
owner:
type: string
validationPlan:
type: array
minItems: 1
items:
type: string
BudgetRecoverySnapshot:
type: object
additionalProperties: false
required:
- snapshotId
- capturedAt
- wallClockSeconds
- classificationTotals
- familyTotals
- slowestEntries
- artifactPaths
- budgetStatus
properties:
snapshotId:
type: string
capturedAt:
type: string
format: date-time
wallClockSeconds:
type: number
minimum: 0
classificationTotals:
type: array
minItems: 1
items:
type: object
additionalProperties: false
required:
- classificationId
- totalWallClockSeconds
properties:
classificationId:
$ref: '#/components/schemas/ClassificationId'
totalWallClockSeconds:
type: number
minimum: 0
familyTotals:
type: array
minItems: 1
items:
type: object
additionalProperties: false
required:
- familyId
- totalWallClockSeconds
properties:
familyId:
type: string
totalWallClockSeconds:
type: number
minimum: 0
slowestEntries:
type: array
minItems: 1
items:
type: object
additionalProperties: false
required:
- label
- wallClockSeconds
properties:
label:
type: string
wallClockSeconds:
type: number
minimum: 0
artifactPaths:
type: object
additionalProperties: false
required:
- summary
- budget
- report
properties:
summary:
type: string
pattern: ^storage/logs/test-lanes/
budget:
type: string
pattern: ^storage/logs/test-lanes/
report:
type: string
pattern: ^storage/logs/test-lanes/
budgetStatus:
$ref: '#/components/schemas/BudgetStatus'
BudgetOutcomeRecord:
type: object
additionalProperties: false
required:
- outcomeId
- decisionStatus
- finalThresholdSeconds
- finalMeasuredSeconds
- deltaSeconds
- deltaPercent
- remainingOpenFamilies
- justification
properties:
outcomeId:
type: string
decisionStatus:
$ref: '#/components/schemas/BudgetDecisionStatus'
finalThresholdSeconds:
type: number
exclusiveMinimum: 0
finalMeasuredSeconds:
type: number
minimum: 0
deltaSeconds:
type: number
deltaPercent:
type: number
remainingOpenFamilies:
type: array
items:
type: string
justification:
type: string
followUpDebt:
type: array
items:
type: string
HeavyAuthorGuidanceRule:
type: object
additionalProperties: false
required:
- ruleId
- whenToUse
- requiredDecision
- antiPattern
- preferredOutcome
properties:
ruleId:
type: string
whenToUse:
type: string
requiredDecision:
type: string
antiPattern:
type: string
preferredOutcome:
type: string

View File

@ -0,0 +1,565 @@
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"$id": "https://tenantatlas.local/schemas/heavy-governance-hotspot-inventory.schema.json",
"title": "HeavyGovernanceHotspotInventory",
"type": "object",
"additionalProperties": false,
"required": [
"version",
"artifactDirectory",
"budgetContract",
"hotspotInventory",
"decompositionRecords",
"slimmingDecisions",
"budgetSnapshots",
"budgetOutcome",
"authorGuidance"
],
"properties": {
"version": {
"type": "integer",
"minimum": 1
},
"artifactDirectory": {
"type": "string",
"const": "storage/logs/test-lanes"
},
"budgetContract": {
"$ref": "#/$defs/budgetContract"
},
"hotspotInventory": {
"type": "array",
"minItems": 5,
"items": {
"$ref": "#/$defs/hotspotInventoryRecord"
},
"allOf": [
{
"contains": {
"type": "object",
"required": ["familyId"],
"properties": {
"familyId": {
"const": "baseline-profile-start-surfaces"
}
}
}
},
{
"contains": {
"type": "object",
"required": ["familyId"],
"properties": {
"familyId": {
"const": "findings-workflow-surfaces"
}
}
}
},
{
"contains": {
"type": "object",
"required": ["familyId"],
"properties": {
"familyId": {
"const": "finding-bulk-actions-workflow"
}
}
}
}
]
},
"decompositionRecords": {
"type": "array",
"minItems": 3,
"items": {
"$ref": "#/$defs/decompositionRecord"
}
},
"slimmingDecisions": {
"type": "array",
"minItems": 3,
"items": {
"$ref": "#/$defs/slimmingDecision"
}
},
"budgetSnapshots": {
"type": "array",
"minItems": 2,
"items": {
"$ref": "#/$defs/budgetSnapshot"
}
},
"budgetOutcome": {
"$ref": "#/$defs/budgetOutcome"
},
"authorGuidance": {
"type": "array",
"minItems": 4,
"items": {
"$ref": "#/$defs/authorGuidanceRule"
}
}
},
"$defs": {
"classificationId": {
"type": "string",
"enum": [
"ui-workflow",
"surface-guard",
"discovery-heavy"
]
},
"costDriverCategory": {
"type": "string",
"enum": [
"overbroad",
"redundant",
"discovery-heavy",
"workflow-heavy",
"surface-heavy",
"helper-driven",
"fixture-driven",
"intentionally-heavy"
]
},
"budgetDecisionStatus": {
"type": "string",
"enum": [
"pending",
"recovered",
"recalibrated"
]
},
"hotspotStatus": {
"type": "string",
"enum": [
"seeded",
"decomposed",
"slimmed",
"retained",
"follow-up"
]
},
"priorityTier": {
"type": "string",
"enum": [
"primary",
"secondary",
"residual"
]
},
"residualCostSource": {
"type": "string",
"enum": [
"family-breadth",
"helper-driven",
"fixture-driven",
"mixed",
"intentional-depth"
]
},
"recommendedAction": {
"type": "string",
"enum": [
"split-family",
"centralize-work",
"narrow-assertions",
"retain-as-heavy",
"route-follow-up"
]
},
"slimmingDecisionType": {
"type": "string",
"enum": [
"split",
"centralize",
"trim-duplicate-work",
"retain",
"follow-up"
]
},
"budgetStatus": {
"type": "string",
"enum": [
"within-budget",
"warning",
"over-budget"
]
},
"budgetContract": {
"type": "object",
"additionalProperties": false,
"required": [
"laneId",
"summaryThresholdSeconds",
"evaluationThresholdSeconds",
"normalizedThresholdSeconds",
"baselineSource",
"enforcementLevel",
"lifecycleState",
"decisionStatus"
],
"properties": {
"laneId": {
"type": "string",
"const": "heavy-governance"
},
"summaryThresholdSeconds": {
"type": "number",
"exclusiveMinimum": 0
},
"evaluationThresholdSeconds": {
"type": "number",
"exclusiveMinimum": 0
},
"normalizedThresholdSeconds": {
"type": "number",
"exclusiveMinimum": 0
},
"baselineSource": {
"type": "string"
},
"enforcementLevel": {
"type": "string",
"enum": ["report-only", "warn", "hard-fail"]
},
"lifecycleState": {
"type": "string",
"enum": ["draft", "documented", "recalibrated"]
},
"reconciliationRationale": {
"type": "string"
},
"decisionStatus": {
"$ref": "#/$defs/budgetDecisionStatus"
}
}
},
"hotspotInventoryRecord": {
"type": "object",
"additionalProperties": false,
"required": [
"familyId",
"classificationId",
"purpose",
"measuredSeconds",
"hotspotFiles",
"costDriverCategory",
"priorityTier",
"status"
],
"properties": {
"familyId": {
"type": "string",
"minLength": 1
},
"classificationId": {
"$ref": "#/$defs/classificationId"
},
"purpose": {
"type": "string",
"minLength": 1
},
"measuredSeconds": {
"type": "number",
"minimum": 0
},
"hotspotFiles": {
"type": "array",
"minItems": 1,
"items": {
"type": "string",
"minLength": 1
}
},
"costDriverCategory": {
"$ref": "#/$defs/costDriverCategory"
},
"priorityTier": {
"$ref": "#/$defs/priorityTier"
},
"currentBudgetSeconds": {
"type": "number",
"minimum": 0
},
"status": {
"$ref": "#/$defs/hotspotStatus"
}
}
},
"decompositionRecord": {
"type": "object",
"additionalProperties": false,
"required": [
"familyId",
"trustType",
"requiredBreadth",
"duplicateWorkSources",
"residualCostSource",
"recommendedAction",
"notes"
],
"properties": {
"familyId": {
"type": "string"
},
"trustType": {
"type": "string",
"enum": ["workflow-trust", "surface-trust", "guard-trust", "discovery-trust"]
},
"requiredBreadth": {
"type": "string",
"minLength": 1
},
"duplicateWorkSources": {
"type": "array",
"items": {
"type": "string"
}
},
"duplicateWorkEstimateSeconds": {
"type": "number",
"minimum": 0
},
"residualCostSource": {
"$ref": "#/$defs/residualCostSource"
},
"recommendedAction": {
"$ref": "#/$defs/recommendedAction"
},
"notes": {
"type": "string",
"minLength": 1
}
}
},
"slimmingDecision": {
"type": "object",
"additionalProperties": false,
"required": [
"familyId",
"decisionType",
"guardPreservationPlan",
"owner",
"validationPlan"
],
"properties": {
"familyId": {
"type": "string"
},
"decisionType": {
"$ref": "#/$defs/slimmingDecisionType"
},
"scope": {
"type": "array",
"items": {
"type": "string"
}
},
"guardPreservationPlan": {
"type": "string",
"minLength": 1
},
"expectedDeltaSeconds": {
"type": "number"
},
"owner": {
"type": "string",
"minLength": 1
},
"validationPlan": {
"type": "array",
"minItems": 1,
"items": {
"type": "string"
}
}
}
},
"budgetSnapshot": {
"type": "object",
"additionalProperties": false,
"required": [
"snapshotId",
"capturedAt",
"wallClockSeconds",
"classificationTotals",
"familyTotals",
"slowestEntries",
"artifactPaths",
"budgetStatus"
],
"properties": {
"snapshotId": {
"type": "string",
"minLength": 1
},
"capturedAt": {
"type": "string",
"format": "date-time"
},
"wallClockSeconds": {
"type": "number",
"minimum": 0
},
"classificationTotals": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["classificationId", "totalWallClockSeconds"],
"properties": {
"classificationId": {
"$ref": "#/$defs/classificationId"
},
"totalWallClockSeconds": {
"type": "number",
"minimum": 0
}
}
}
},
"familyTotals": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["familyId", "totalWallClockSeconds"],
"properties": {
"familyId": {
"type": "string"
},
"totalWallClockSeconds": {
"type": "number",
"minimum": 0
}
}
}
},
"slowestEntries": {
"type": "array",
"minItems": 1,
"items": {
"type": "object",
"additionalProperties": false,
"required": ["label", "wallClockSeconds"],
"properties": {
"label": {
"type": "string"
},
"wallClockSeconds": {
"type": "number",
"minimum": 0
}
}
}
},
"artifactPaths": {
"type": "object",
"additionalProperties": false,
"required": ["summary", "budget", "report"],
"properties": {
"summary": {
"type": "string",
"pattern": "^storage/logs/test-lanes/"
},
"budget": {
"type": "string",
"pattern": "^storage/logs/test-lanes/"
},
"report": {
"type": "string",
"pattern": "^storage/logs/test-lanes/"
}
}
},
"budgetStatus": {
"$ref": "#/$defs/budgetStatus"
}
}
},
"budgetOutcome": {
"type": "object",
"additionalProperties": false,
"required": [
"outcomeId",
"decisionStatus",
"finalThresholdSeconds",
"finalMeasuredSeconds",
"deltaSeconds",
"deltaPercent",
"remainingOpenFamilies",
"justification"
],
"properties": {
"outcomeId": {
"type": "string",
"minLength": 1
},
"decisionStatus": {
"$ref": "#/$defs/budgetDecisionStatus"
},
"finalThresholdSeconds": {
"type": "number",
"exclusiveMinimum": 0
},
"finalMeasuredSeconds": {
"type": "number",
"minimum": 0
},
"deltaSeconds": {
"type": "number"
},
"deltaPercent": {
"type": "number"
},
"remainingOpenFamilies": {
"type": "array",
"items": {
"type": "string"
}
},
"justification": {
"type": "string",
"minLength": 1
},
"followUpDebt": {
"type": "array",
"items": {
"type": "string"
}
}
}
},
"authorGuidanceRule": {
"type": "object",
"additionalProperties": false,
"required": ["ruleId", "whenToUse", "requiredDecision", "antiPattern", "preferredOutcome"],
"properties": {
"ruleId": {
"type": "string",
"minLength": 1
},
"whenToUse": {
"type": "string",
"minLength": 1
},
"requiredDecision": {
"type": "string",
"minLength": 1
},
"antiPattern": {
"type": "string",
"minLength": 1
},
"preferredOutcome": {
"type": "string",
"minLength": 1
}
}
}
}
}

View File

@ -0,0 +1,193 @@
# Data Model: Heavy Governance Lane Cost Reduction
This feature does not introduce new runtime database tables. The data-model work formalizes repository-level governance objects that describe how the heavy-governance lane is inventoried, decomposed, slimmed, and evaluated against a single explicit budget contract. It builds directly on the existing Spec 206 to 208 lane manifest and report artifacts.
## 1. Heavy Governance Budget Contract
### Purpose
Represents the deliberate budget rule for the heavy-governance lane, including the authoritative pre-normalization `300s` summary threshold, the legacy `200s` detailed budget-target evaluation, and the final reconciled threshold published by Spec 209.
### Fields
- `laneId`: expected to be `heavy-governance`
- `summaryThresholdSeconds`: current authoritative pre-normalization lane-level threshold used by the summary artifact
- `evaluationThresholdSeconds`: legacy threshold currently used in detailed budget evaluations until normalization is complete
- `normalizedThresholdSeconds`: the single threshold that will be treated as authoritative after Spec 209
- `baselineSource`: where the budget came from, such as `measured-lane`
- `enforcementLevel`: `report-only`, `warn`, or `hard-fail`
- `lifecycleState`: `draft`, `documented`, or `recalibrated`
- `reconciliationRationale`: explanation of why the single threshold is correct for the post-Spec-209 lane
- `decisionStatus`: `pending`, `recovered`, or `recalibrated`
### Validation rules
- `laneId` must be `heavy-governance`.
- `summaryThresholdSeconds` and `evaluationThresholdSeconds` must both be captured when they differ, and the authoritative pre-normalization contract must remain identifiable until normalization is complete.
- `normalizedThresholdSeconds` must be present before the rollout is considered complete.
- `decisionStatus = recovered` requires measured runtime less than or equal to `normalizedThresholdSeconds`.
- `decisionStatus = recalibrated` requires a non-empty `reconciliationRationale`.
## 2. Heavy Governance Hotspot Inventory Record
### Purpose
Represents one named heavy-governance family that materially contributes to the lane's runtime and needs explicit review.
### Fields
- `familyId`: stable family identifier
- `classificationId`: current owning class such as `ui-workflow`, `surface-guard`, or `discovery-heavy`
- `purpose`: why the family exists and what trust it provides
- `measuredSeconds`: current measured contribution from the latest heavy-governance report
- `hotspotFiles`: dominant files tied to the family
- `costDriverCategory`: primary cause such as `overbroad`, `redundant`, `discovery-heavy`, `workflow-heavy`, `surface-heavy`, `helper-driven`, `fixture-driven`, or `intentionally-heavy`
- `priorityTier`: `primary`, `secondary`, or `residual`
- `currentBudgetSeconds`: current family-level budget if one exists
- `status`: `seeded`, `decomposed`, `slimmed`, `retained`, or `follow-up`
### Validation rules
- `hotspotFiles` must contain at least one file.
- `priorityTier = primary` is required for the families that explain most of the lane runtime.
- The full hotspot inventory must cover the current top 5 families by runtime, or enough families to explain at least 80% of lane runtime, whichever set is larger.
- `status = slimmed` or `status = retained` requires a corresponding decomposition record.
- If `currentBudgetSeconds` exists, it must match the checked-in family budget contract.
## 3. Family Cost Decomposition Record
### Purpose
Represents the internal analysis for a targeted hotspot family so reviewers can see what part of the family is necessary and what part is duplicated or accidental.
### Fields
- `familyId`: referenced hotspot family
- `trustType`: primary trust delivered, such as `workflow-trust`, `surface-trust`, `guard-trust`, or `discovery-trust`
- `requiredBreadth`: what breadth is genuinely needed for product trust
- `duplicateWorkSources`: repeated work sources such as `repeated-livewire-mounts`, `header-action-gating-matrix`, `filter-state-persistence`, `audit-fan-out`, `resource-discovery-pass`, or `helper-graph-build`
- `duplicateWorkEstimateSeconds`: optional estimate of removable cost
- `residualCostSource`: `family-breadth`, `helper-driven`, `fixture-driven`, `mixed`, or `intentional-depth`
- `recommendedAction`: `split-family`, `centralize-work`, `narrow-assertions`, `retain-as-heavy`, or `route-follow-up`
- `notes`: reviewer-readable explanation
### Validation rules
- Every `primary` hotspot family must have one decomposition record.
- `recommendedAction = route-follow-up` requires `residualCostSource` to be `helper-driven`, `fixture-driven`, or `mixed`.
- `recommendedAction = retain-as-heavy` requires `residualCostSource = intentional-depth`.
- `duplicateWorkEstimateSeconds` may be omitted when cost is truly intentional, but the reason must be explicit.
## 4. Heavy Family Slimming Decision
### Purpose
Represents the implementation-facing decision taken for a hotspot family after decomposition.
### Fields
- `familyId`: referenced hotspot family
- `decisionType`: `split`, `centralize`, `trim-duplicate-work`, `retain`, or `follow-up`
- `scope`: list of files, helpers, or manifest entries touched by the decision
- `guardPreservationPlan`: how the original governance trust remains protected
- `expectedDeltaSeconds`: estimated improvement if known
- `owner`: responsible maintainer or team role
- `validationPlan`: focused tests or lane reruns needed to validate the decision
### Validation rules
- Every `decisionType` other than `retain` must include at least one item in `scope`.
- `guardPreservationPlan` is mandatory for all decisions.
- `follow-up` decisions must name the residual cause and target seam.
- `retain` decisions must still reference a validation plan showing why the retained heaviness is acceptable.
## 5. Budget Recovery Snapshot
### Purpose
Represents one snapshot in the before-and-after lane measurement pair used to prove recovery or justify recalibration.
### Fields
- `snapshotId`: stable identifier such as `pre-slimming` or `post-slimming`
- `capturedAt`: ISO timestamp
- `wallClockSeconds`: measured heavy-governance wall-clock time
- `classificationTotals`: totals by classification
- `familyTotals`: totals by family
- `slowestEntries`: top slowest test entries
- `artifactPaths`: references to summary, report, and budget artifacts
- `budgetStatus`: `within-budget`, `warning`, or `over-budget`
### Validation rules
- At least two snapshots are expected for a complete rollout: baseline and post-change.
- `artifactPaths` must stay under `storage/logs/test-lanes`.
- `familyTotals` must include the targeted hotspot families.
- Summary, budget, and report artifacts captured for the same snapshot must not disagree on the authoritative threshold or budget outcome classification.
## 6. Budget Outcome Record
### Purpose
Represents the final explicit outcome required by Spec 209.
### Fields
- `outcomeId`: stable identifier
- `decisionStatus`: `recovered` or `recalibrated`
- `finalThresholdSeconds`: authoritative heavy-governance threshold after the rollout
- `finalMeasuredSeconds`: measured post-change runtime
- `deltaSeconds`: change from the baseline snapshot
- `deltaPercent`: percentage change from the baseline snapshot
- `remainingOpenFamilies`: families still above expected cost or still awaiting follow-up
- `justification`: human-readable explanation of the decision
- `followUpDebt`: optional residual items that remain outside the current scope
### Validation rules
- `decisionStatus = recovered` requires `finalMeasuredSeconds <= finalThresholdSeconds`.
- `decisionStatus = recalibrated` requires a non-empty `justification` explaining why the new threshold is honest.
- `remainingOpenFamilies` may be non-empty only when their residual status is explicit.
## 7. Heavy Author Guidance Rule
### Purpose
Represents a short reviewer or author rule for future heavy-governance tests.
### Fields
- `ruleId`: stable identifier
- `whenToUse`: the situation the rule applies to
- `requiredDecision`: what the author or reviewer must decide
- `antiPattern`: what overbroad behavior the rule prevents
- `preferredOutcome`: the intended family or separation behavior
### Validation rules
- Guidance must cover at least: when to create a new heavy family, when to reuse an existing family, when a test is too broad, and when discovery, workflow, and surface trust must be separated.
## 8. Current Measured Inventory Snapshot
### Current dominant families
- `baseline-profile-start-surfaces``98.112193s``ui-workflow` — currently the largest heavy-governance family
- `action-surface-contract``40.841552s``surface-guard`
- `ops-ux-governance``38.794861s``surface-guard`
- `findings-workflow-surfaces``36.459493s``ui-workflow`
- `finding-bulk-actions-workflow``26.491446s``ui-workflow`
- `workspace-settings-slice-management``21.740839s``ui-workflow`
### Current classification totals
- `ui-workflow``190.606431s`
- `surface-guard``106.845887s`
- `discovery-heavy``0.863003s`
### Current budget signals
- Lane summary threshold: `300s` and currently the authoritative pre-normalization contract
- Budget target evaluation threshold: `200s` and currently legacy drift evidence, not a second passing threshold
- Current measured lane wall clock: `318.296962s`
This dual-signal state is intentional input to Spec 209 and must be resolved by the final budget outcome.

View File

@ -0,0 +1,168 @@
# Implementation Plan: Heavy Governance Lane Cost Reduction
**Branch**: `209-heavy-governance-cost` | **Date**: 2026-04-17 | **Spec**: `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/209-heavy-governance-cost/spec.md`
**Input**: Feature specification from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/209-heavy-governance-cost/spec.md`
## Summary
Build Spec 209 on top of the existing Spec 206, 207, and 208 lane infrastructure by using the current heavy-governance artifact set as the baseline, treating the current `300s` lane summary threshold as the authoritative pre-normalization contract while the `200s` `budgetTargets()` signal remains legacy drift evidence to be reconciled, decomposing the dominant heavy families by trust type and duplicated work, targeting the ui-workflow hotspots first, treating surface-guard families as intentional heavy checks unless repeatable redundancy is proven, and ending with explicit budget recovery or explicit recalibration evidence without moving heavy cost back into lighter lanes.
## Technical Context
**Language/Version**: PHP 8.4.15
**Primary Dependencies**: Laravel 12, Pest v4, PHPUnit 12, Filament v5, Livewire v4, Laravel Sail
**Storage**: SQLite `:memory:` for the default test environment, mixed database strategy for some heavy-governance families as declared in `TestLaneManifest`, and existing lane artifacts under the app-root contract path `storage/logs/test-lanes`
**Testing**: Pest unit, feature, browser, architecture, and guard suites run through Sail-wrapped `artisan test`; heavy-lane selection and reporting already flow through `Tests\Support\TestLaneManifest`, `Tests\Support\TestLaneBudget`, `Tests\Support\TestLaneReport`, `tests/Pest.php`, and the repo-root wrappers `scripts/platform-test-lane` and `scripts/platform-test-report`
**Target Platform**: Laravel monorepo application in `apps/platform`, executed locally through Sail and later enforced in shared CI
**Project Type**: Monorepo with a Laravel platform app and separate Astro website; this feature is scoped to platform test-governance infrastructure
**Performance Goals**: Recover the heavy-governance lane from the current `318.296962s` run to the authoritative pre-normalization heavy-lane threshold of `300s`, or explicitly recalibrate that threshold after evidence is gathered; explain at least 80% of heavy-lane runtime through named families; reduce duplicate work or accidental breadth in the top hotspot families without reducing governance trust
**Constraints**: Sail-first commands only; no new product routes, assets, runtime services, or dependencies; no browser-lane redesign; no CI-matrix rollout; no lane-hiding by moving heavy families into Confidence or Fast Feedback; preserve Heavy Governance lane membership for touched families unless a non-budget, spec-backed rationale is recorded; treat the current `300s` lane summary threshold as the authoritative pre-normalization contract while the `200s` lane budget-target evaluation remains legacy drift evidence to be normalized
**Scale/Scope**: Current heavy-governance reporting attributes 14 named families; `ui-workflow` accounts for `190.606431s`, `surface-guard` for `106.845887s`, and `discovery-heavy` for `0.863003s`; the dominant family hotspots are `baseline-profile-start-surfaces` (`98.112193s`), `action-surface-contract` (`40.841552s`), `ops-ux-governance` (`38.794861s`), `findings-workflow-surfaces` (`36.459493s`), `finding-bulk-actions-workflow` (`26.491446s`), and `workspace-settings-slice-management` (`21.740839s`)
### Filament v5 Implementation Notes
- **Livewire v4.0+ compliance**: Preserved. This feature changes only repository test-governance around Filament and Livewire-heavy tests, not runtime Filament or Livewire behavior.
- **Provider registration location**: Unchanged. Existing panel providers remain registered in `bootstrap/providers.php`.
- **Global search rule**: No globally searchable resources are added or modified. Discovery-heavy parity tests may be reclassified or slimmed, but runtime global-search behavior is unchanged.
- **Destructive actions**: No runtime destructive actions are introduced. Any tests touched by this feature continue to validate existing confirmation and authorization behavior only.
- **Asset strategy**: No panel-only or shared assets are added. Existing `filament:assets` deployment behavior remains unchanged.
- **Testing plan**: Add or update Pest guard coverage for heavy-hotspot inventory integrity, budget-signal consistency, family decomposition records, heavy-lane budget outcome reporting, and targeted hotspot family regression checks. Focused validation should cover the targeted families plus the heavy-governance lane wrapper.
## Constitution Check
*GATE: Must pass before Phase 0 research. Re-check after Phase 1 design.*
- Inventory-first: PASS. No Inventory, snapshots, or backup truth is changed.
- Read/write separation: PASS. The feature only changes repository test-governance behavior and introduces no end-user mutation path.
- Graph contract path: PASS. No Graph calls, contract-registry changes, or provider runtime integrations are added.
- Deterministic capabilities: PASS. No capability resolver or authorization registry changes.
- RBAC-UX, workspace isolation, tenant isolation: PASS. No runtime routes, policies, global search availability, or tenant/workspace enforcement semantics are changed.
- Run observability and Ops-UX: PASS. Reporting remains filesystem-based through the existing lane tooling and does not introduce `OperationRun` behavior.
- Data minimization: PASS. Heavy-lane inventories and reports remain repo-local and contain no secrets or customer payloads.
- Proportionality and bloat control: PASS WITH LIMITS. The only new semantic layer is a narrow repo-local hotspot inventory and decomposition model. The plan explicitly avoids a broader framework and keeps family changes tied to measured cost and guard preservation.
- TEST-TRUTH-001: PASS WITH WORK. The plan must prove that runtime gains come from removing duplicated work or accidental breadth rather than from quietly deleting governance trust.
- Filament/UI constitutions: PASS / NOT APPLICABLE. No operator-facing UI, action-surface runtime contract, badge semantics, or panel IA is changed.
**Phase 0 Gate Result**: PASS
- The feature remains bounded to repository test governance, hotspot evidence, family decomposition, and budget normalization.
- No new runtime persistence, product routes, panels, assets, or Graph seams are introduced.
- The chosen approach extends the existing Spec 206 to 208 tooling instead of creating a second heavy-lane governance system.
## Project Structure
### Documentation (this feature)
```text
specs/209-heavy-governance-cost/
├── plan.md
├── research.md
├── data-model.md
├── quickstart.md
├── contracts/
│ ├── heavy-governance-hotspot-inventory.schema.json
│ └── heavy-governance-cost-recovery.logical.openapi.yaml
└── tasks.md
```
### Source Code (repository root)
```text
apps/
├── platform/
│ ├── composer.json
│ ├── tests/
│ │ ├── Pest.php
│ │ ├── Support/
│ │ │ ├── TestLaneBudget.php
│ │ │ ├── TestLaneManifest.php
│ │ │ └── TestLaneReport.php
│ │ ├── Feature/
│ │ │ ├── Baselines/
│ │ │ ├── Drift/
│ │ │ ├── Filament/
│ │ │ ├── Findings/
│ │ │ ├── Guards/
│ │ │ ├── OpsUx/
│ │ │ ├── Rbac/
│ │ │ └── SettingsFoundation/
│ └── storage/logs/test-lanes/
├── website/
└── ...
scripts/
├── platform-test-lane
└── platform-test-report
```
**Structure Decision**: Keep implementation concentrated in the existing platform test-governance seams: `apps/platform/tests/Support/TestLaneManifest.php` for hotspot inventory, family budgets, and budget-signal normalization; `apps/platform/tests/Support/TestLaneReport.php` for before-and-after attribution and explicit budget outcomes; focused heavy families under `apps/platform/tests/Feature/Baselines`, `apps/platform/tests/Feature/Filament`, `apps/platform/tests/Feature/Findings`, `apps/platform/tests/Feature/Guards`, `apps/platform/tests/Feature/OpsUx`, and `apps/platform/tests/Feature/SettingsFoundation`; and the existing repo-root wrappers for measurement. Planning artifacts stay inside `specs/209-heavy-governance-cost`.
## Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|-----------|------------|-------------------------------------|
| None | Not applicable | Not applicable |
## Proportionality Review
- **Current operator problem**: Maintainers and reviewers cannot tell which heavy-governance families are legitimately expensive, which are redundant, and which are simply overbroad, so the lane remains over budget without a stable correction path.
- **Existing structure is insufficient because**: Spec 208 established heavy-family ownership and attribution, but it did not yet decompose the internal cost of the dominant families or reconcile the current heavy-budget mismatch.
- **Narrowest correct implementation**: Extend the existing lane manifest, report artifacts, and heavy-family catalog with hotspot decomposition, explicit residual-cause records, a budget-outcome record, and targeted guidance for future heavy tests.
- **Ownership cost created**: The repo must maintain hotspot decomposition notes, the reconciled heavy-budget contract, and author or reviewer guidance as heavy families evolve.
- **Alternative intentionally rejected**: Local one-off runtime trims or moving families back into lighter lanes, because those approaches hide cost instead of making it governable.
- **Release truth**: Current-release repository truth and the necessary stabilization step before CI budget enforcement.
## Phase 0 — Research (complete)
- Output: [research.md](./research.md)
- Resolved key decisions:
- Reuse the existing heavy-governance manifest, budget, report, and wrapper seams rather than creating a second heavy-lane system.
- Treat the current heavy-governance artifact set as the baseline, specifically `318.296962s` wall-clock against the authoritative pre-normalization `300s` lane summary threshold.
- Make the dual heavy-budget signal explicit: the lane summary uses `300s` as the authoritative pre-normalization contract, while `budgetTargets()` still evaluates the lane against `200s` as legacy drift evidence that must be normalized.
- Prioritize `baseline-profile-start-surfaces`, `findings-workflow-surfaces`, and `finding-bulk-actions-workflow` as the first slimming targets because the `ui-workflow` classification currently dominates the lane.
- Decompose targeted families by repeated work before splitting files mechanically.
- Record helper-driven or fixture-driven cost as explicit residual debt when that is the real cause.
- Treat `action-surface-contract` and `ops-ux-governance` as intentional heavy second-wave candidates unless repeated duplication is proven.
- Keep before-and-after evidence inside the existing heavy-governance artifact set under `storage/logs/test-lanes`.
- End the feature with explicit recovery or explicit recalibration, not an implicit “still heavy” state.
## Phase 1 — Design & Contracts (complete)
- Output: [data-model.md](./data-model.md) formalizes the hotspot inventory, per-family cost decomposition, slimming decisions, dual-budget contract, explicit budget outcome, and author-guidance rule set.
- Output: [contracts/heavy-governance-hotspot-inventory.schema.json](./contracts/heavy-governance-hotspot-inventory.schema.json) defines the checked-in schema for the heavy-governance hotspot inventory, decomposition records, budget signals, and final outcome contract.
- Output: [contracts/heavy-governance-cost-recovery.logical.openapi.yaml](./contracts/heavy-governance-cost-recovery.logical.openapi.yaml) captures the logical contract for reading hotspots, recording family decomposition, evaluating budget outcomes, and publishing reviewer guidance.
- Output: [quickstart.md](./quickstart.md) provides the rollout order, validation commands, and review checkpoints for the cost-recovery work.
### Post-design Constitution Re-check
- PASS: No runtime routes, panels, authorization planes, or Graph seams are introduced.
- PASS: The new hotspot inventory and budget-outcome records are repo-local, directly justified by current lane drift, and bounded to existing test-governance seams.
- PASS: The design prefers extending existing manifest, guard, and reporting seams over adding a second governance framework.
- PASS WITH WORK: The final implementation must normalize the conflicting heavy-lane budget signals so reviewers see one intentional contract instead of two competing thresholds.
- PASS WITH WORK: The final implementation must show that heavy runtime improvements came from duplicate-work removal or narrower family scope, not from hidden trust reduction.
## Phase 2 — Implementation Planning
`tasks.md` should cover:
- Refreshing the current heavy-governance baseline artifact set through the standard lane wrappers before any family edits.
- Building a checked-in hotspot inventory that covers the current top 5 families by runtime, or enough families to explain at least 80% of lane runtime, whichever set is larger.
- Auditing `baseline-profile-start-surfaces`, `findings-workflow-surfaces`, and `finding-bulk-actions-workflow` for repeated Livewire mounts, gating matrices, filter persistence, audit fan-out, and helper-driven cost.
- Deciding for each targeted family whether the right fix is splitting, centralizing repeated work, or recording explicit intentional heaviness.
- Auditing second-wave surface-guard families such as `action-surface-contract` and `ops-ux-governance` for internal redundancy only after the workflow-heavy hotspots are addressed.
- Extending `TestLaneManifest` and `TestLaneReport` so hotspot inventory, residual causes, and budget outcomes stay reviewable and attributable.
- Adding or updating guard tests that protect budget-signal consistency, hotspot-inventory integrity, the top-5-or-80%-coverage rule, lane-membership invariants for touched heavy families, and future heavy-family authoring discipline.
- Normalizing the heavy-governance budget contract from the authoritative pre-normalization `300s` summary threshold and the legacy `200s` `budgetTargets()` signal to one deliberate rule after the hotspot inventory and slimming pass have established the honest lane shape.
- Rerunning the heavy-governance lane and its focused hotspot packs to produce post-change summary, report, and budget artifacts.
- Recording the final budget decision as explicit recovery within the authoritative threshold or explicit recalibration with evidence.
### Contract Implementation Note
- The JSON schema is schema-first and repository-tooling-oriented. It defines what the checked-in hotspot inventory, decomposition records, budget contract, and final budget outcome must express even if the first implementation remains PHP arrays in `TestLaneManifest` and JSON output from `TestLaneReport`.
- The OpenAPI file is logical rather than transport-prescriptive. It documents the expected semantics of hotspot inspection, decomposition review, budget-outcome evaluation, and author guidance for in-process repository tooling.
- The design intentionally avoids introducing a new runtime service, new database table, or new artifact root outside the existing `storage/logs/test-lanes` contract.
### Deployment Sequencing Note
- No database migration is planned.
- No asset publish step changes.
- The rollout should start with a fresh heavy-governance baseline capture through the standard wrappers, then hotspot decomposition, then family-level slimming or centralization, then budget-signal normalization, and finally a rerun whose summary, budget, and report artifacts agree on one authoritative threshold and one explicit outcome.

View File

@ -0,0 +1,151 @@
# Quickstart: Heavy Governance Lane Cost Reduction
## Goal
Stabilize the heavy-governance lane so its dominant costs are visible, intentionally sliced, and either brought back within the authoritative heavy-lane budget, which starts at `300s` before normalization, or consciously recalibrated with evidence.
## Current Outcome
The latest honest rerun ends in explicit recalibration rather than recovery.
| Signal | Current value | Meaning |
|-------|---------------|---------|
| Final wall clock | `329.305382s` | Current heavy-governance lane runtime after the slimming pass |
| Final authoritative threshold | `330s` | Normalized threshold used consistently by summary, budget, and report artifacts |
| Outcome | `recalibrated` | The lane no longer has dual active thresholds, but it still needs a slightly higher honest contract |
| Baseline delta | `+11.008420s` (`+3.458905%`) | Current rerun versus the preserved pre-slimming baseline |
| Legacy drift signal | `200s` | Preserved as historical detailed-budget evidence only |
| Pre-normalization summary threshold | `300s` | Preserved as the rollout acceptance contract before normalization |
The final reconciled rationale is: workflow-heavy duplication was reduced, but the settled lane still retains intentional surface-guard depth plus the workspace settings residual helper cost, so the contract is now `330s`.
## Implementation Order
1. Capture a fresh heavy-governance baseline through the existing lane wrappers and preserve the current summary, report, and budget artifacts. The checked-in wrappers now support `--capture-baseline` for heavy-governance baseline copies.
2. Build or refresh the hotspot inventory for the current top 5 families by runtime, or enough families to explain at least 80% of lane runtime, whichever set is larger.
3. Decompose the primary ui-workflow hotspots first: `baseline-profile-start-surfaces`, `findings-workflow-surfaces`, and `finding-bulk-actions-workflow`.
4. Decide per family whether the right move is split, centralize repeated work, trim duplicate assertions, or retain as intentionally heavy.
5. Audit second-wave surface-guard families such as `action-surface-contract` and `ops-ux-governance` only after the workflow-heavy hotspots are understood.
6. Extend or adjust manifest and report seams so decomposition, residual causes, and the final budget outcome remain visible.
7. Normalize the heavy-governance budget contract so the authoritative pre-normalization `300s` summary threshold and the legacy `200s` budget-target evaluation describe one intentional rule after the honest lane shape is established.
8. Rerun the focused hotspot packs and the full heavy-governance lane.
9. Record the final outcome as budget recovery or explicit recalibration and add short reviewer guidance for future heavy tests.
## Suggested Code Touches
```text
apps/platform/tests/Support/TestLaneBudget.php
apps/platform/tests/Support/TestLaneManifest.php
apps/platform/tests/Support/TestLaneReport.php
apps/platform/tests/Feature/Baselines/*
apps/platform/tests/Feature/Filament/BaselineActionAuthorizationTest.php
apps/platform/tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php
apps/platform/tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php
apps/platform/tests/Feature/Findings/*
apps/platform/tests/Feature/Guards/ActionSurfaceContractTest.php
apps/platform/tests/Feature/OpsUx/*
apps/platform/tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php
scripts/platform-test-lane
scripts/platform-test-report
```
## Validation Flow
Use the existing checked-in lane wrappers first:
```bash
./scripts/platform-test-report heavy-governance --capture-baseline
./scripts/platform-test-lane heavy-governance --capture-baseline
./scripts/platform-test-report heavy-governance
./scripts/platform-test-lane heavy-governance
./scripts/platform-test-report heavy-governance
cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent
```
Keep the implementation loop tight with the most relevant focused suites before rerunning the whole lane:
```bash
cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament --filter=BaselineProfileCaptureStartSurfaceTest
cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament --filter=BaselineProfileCompareStartSurfaceTest
cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Filament --filter=BaselineActionAuthorizationTest
cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings --filter=FindingBulkActionsTest
cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Findings --filter=FindingWorkflow
cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Guards --filter=ActionSurfaceContractTest
cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/OpsUx
```
## Current Baseline
Use the checked-in heavy-governance artifacts under `apps/platform/storage/logs/test-lanes` as the starting point.
| Signal | Current value | Planning note |
|-------|---------------|---------------|
| Lane wall clock | `318.296962s` | Current measured overrun |
| Lane summary threshold | `300s` | Authoritative pre-normalization contract for Spec 209 acceptance |
| Budget target evaluation threshold | `200s` | Legacy drift evidence that must remain visible until the contract is normalized |
| `ui-workflow` total | `190.606431s` | Dominant class; first slimming target |
| `surface-guard` total | `106.845887s` | Second-wave analysis target |
| `discovery-heavy` total | `0.863003s` | Already bounded; not the main cost problem |
## Current Canonical Inventory
The canonical inventory now covers six families because the top five alone do not clear the required `80%` runtime threshold.
| Family | Baseline measured time | Current status | Driver |
|-------|-------------------------|----------------|--------|
| `baseline-profile-start-surfaces` | `98.112193s` | `slimmed` | workflow-heavy |
| `action-surface-contract` | `40.841552s` | `retained` | intentionally-heavy |
| `ops-ux-governance` | `38.794861s` | `retained` | intentionally-heavy |
| `findings-workflow-surfaces` | `36.459493s` | `slimmed` | workflow-heavy |
| `finding-bulk-actions-workflow` | `26.491446s` | `slimmed` | redundant |
| `workspace-settings-slice-management` | `21.740839s` | `follow-up` | helper-driven |
Together these six families explain `263.617244s`, or `80.052516%`, of the latest heavy-governance runtime.
## Latest Rerun Hotspots
| Family | Latest measured time | Current intent |
|-------|------------------------|----------------|
| `baseline-profile-start-surfaces` | `101.895415s` | Still dominant after slimming; trust retained |
| `action-surface-contract` | `38.323501s` | Intentionally heavy and retained |
| `ops-ux-governance` | `36.497049s` | Intentionally heavy and retained |
| `findings-workflow-surfaces` | `35.990272s` | Slimmed, but still a meaningful workflow-heavy slice |
| `finding-bulk-actions-workflow` | `30.145259s` | Slimmed fixture fan-out, still a top single test family |
| `workspace-settings-slice-management` | `20.765748s` | Recorded as explicit follow-up debt |
## Decomposition Checklist
For each primary hotspot family, answer these questions before changing file structure:
1. What governance trust does this family deliver?
2. What breadth is genuinely required for that trust?
3. Which repeated work sources dominate runtime?
4. Is the main cost family-breadth, helper-driven setup, or fixture-driven setup?
5. Is the correct fix a split, a centralization, a duplicate-assertion trim, or intentional retention?
6. What focused tests and lane reruns prove the change did not hollow out governance trust?
## Reviewer Guidance Targets
The implementation should leave behind short rules that cover:
1. When a new heavy family is justified.
2. When a test should join an existing heavy family instead.
3. When discovery, workflow, and surface trust must be separated.
4. When a family should stay intentionally heavy.
5. When a helper or fixture cost must be recorded as residual debt instead of disguised as family improvement.
The canonical reviewer rules now live in `TestLaneManifest::heavyGovernanceAuthorGuidance()` and are:
1. `heavy-family-reuse-before-creation`
2. `heavy-family-create-only-for-new-trust`
3. `split-discovery-workflow-surface-concerns`
4. `retain-intentional-heavy-depth-explicitly`
5. `record-helper-or-fixture-residuals`
## Exit Criteria
1. The heavy-governance budget contract is normalized to one authoritative threshold, and the summary, budget, and report artifacts do not disagree about it.
2. The primary hotspot families have decomposition records and explicit slimming decisions.
3. The heavy-governance lane has fresh before and after evidence in the standard artifact paths, including inventory coverage for the top 5 families or at least 80% of runtime, whichever is larger.
4. The final outcome is explicit: recovered within the authoritative threshold for the rollout or consciously recalibrated.
5. Reviewer guidance exists for future heavy-family authoring.

View File

@ -0,0 +1,81 @@
# Research: Heavy Governance Lane Cost Reduction
## Decision 1: Reuse the existing heavy-lane governance seams instead of adding a new runner or metadata system
- Decision: Spec 209 should extend the existing `TestLaneManifest`, `TestLaneBudget`, `TestLaneReport`, `scripts/platform-test-lane`, and `scripts/platform-test-report` seams rather than introducing a second planning or reporting framework.
- Rationale: The repository already has checked-in heavy-family attribution, family budgets, lane budgets, and report artifacts under `apps/platform/storage/logs/test-lanes`. The missing work is family decomposition and budget recovery, not missing lane infrastructure.
- Alternatives considered:
- Add a separate heavy-cost analysis runner: rejected because it would duplicate the current test-governance contract.
- Use only ad-hoc profiling commands: rejected because Spec 209 requires repeatable before-and-after evidence and reviewer-visible outputs.
## Decision 2: Treat the feature as a budget-recovery exercise against the current measured heavy-governance overrun
- Decision: The planning baseline for Spec 209 is the current heavy-governance artifact set showing `318.296962` seconds wall-clock versus the authoritative pre-normalization lane summary budget of `300` seconds.
- Rationale: Spec 208 already moved the correct heavy families into the heavy-governance lane. The remaining issue is now an explicit cost-recovery problem, not a classification problem.
- Alternatives considered:
- Re-profile from scratch without using the current heavy artifact: rejected because the current artifact already captures the relevant runtime signal and hotspot attribution.
- Treat the lane as healthy because the overrun is relatively small: rejected because the spec requires an explicit budget outcome, not quiet acceptance of ongoing drift.
## Decision 3: Make the dual heavy-lane budget signal an explicit planning concern
- Decision: Spec 209 should explicitly reconcile the current heavy-governance budget mismatch by treating the lane summary threshold of `300` seconds as the authoritative pre-normalization contract and the separate `budgetTargets()` lane target of `200` seconds as legacy drift evidence until one normalized threshold is published.
- Rationale: The current report summary and the detailed budget evaluation do not describe the same target. A later CI budget-enforcement phase cannot be credible while that inconsistency exists.
- Alternatives considered:
- Ignore the mismatch and optimize only against the 300-second lane summary: rejected because the stricter 200-second target still appears in checked-in budget evaluations and will confuse reviewers.
- Force the lane to 200 seconds immediately: rejected because the spec first needs to determine whether the 200-second target is still realistic for the now-honest heavy lane.
## Decision 4: Prioritize the dominant ui-workflow families before second-wave surface guards
- Decision: The first slimming pass should prioritize `baseline-profile-start-surfaces`, `findings-workflow-surfaces`, and `finding-bulk-actions-workflow`, with `workspace-settings-slice-management` as the next workflow-heavy fallback if more recovery is required.
- Rationale: Current heavy-governance attribution shows `ui-workflow` at `190.606431` seconds, or roughly 60% of lane cost. The three named families together account for about `161.06` seconds and directly align with the spec's required hotspot set.
- Alternatives considered:
- Start with `action-surface-contract`: rejected as the first pass because it is clearly expensive but already documented as an intentional governance guard and may have less removable duplication than the workflow-heavy hotspots.
- Start with all surface-guard families equally: rejected because the current runtime evidence shows ui-workflow as the dominant cost bucket.
## Decision 5: Decompose targeted families by repeated work before splitting files mechanically
- Decision: Each targeted hotspot family should first be decomposed by repeated Livewire mounts, header-action gating matrices, filter-state persistence checks, bulk-action fan-out, evidence or audit verification, and any helper-driven fixture cost before deciding whether to split files.
- Rationale: Spec 209 is about real cost reduction, not cosmetic decomposition. A family can remain overbroad even after being split if the same expensive setup still runs in every resulting file.
- Alternatives considered:
- Split all top families immediately: rejected because that can produce cleaner file boundaries without removing the dominant repeated work.
- Only centralize helper setup without family-level analysis: rejected because some cost may be due to semantic breadth rather than helper shape.
## Decision 6: Record helper-driven or fixture-driven cost as residual debt instead of forcing a family explanation that is not true
- Decision: If a targeted hotspot is found to be dominated by helper, fixture, or support-path cost rather than family breadth, the resulting plan should record that as explicit residual debt and treat it as follow-up work instead of pretending the family itself was narrowed.
- Rationale: The spec requires honest attribution. Mislabeling helper or fixture cost as family-width improvement would create false confidence and make later budget work less reliable.
- Alternatives considered:
- Force all heavy cost into family-width categories: rejected because it would violate the spec's explicit residual-cause requirement.
- Reopen Spec 207 inside Spec 209: rejected because fixture slimming remains a separate concern even when its residual effects appear here.
## Decision 7: Treat `action-surface-contract` and `ops-ux-governance` as intentional heavy families unless decomposition exposes repeatable duplication
- Decision: `action-surface-contract` and `ops-ux-governance` should be treated as second-wave slimming candidates. They remain in heavy-governance by default and should only be narrowed where clear duplicate discovery or repeated governance passes can be shown.
- Rationale: Together they account for `79.636413` seconds and are meaningful heavy governance checks. They may still contain removable redundancy, but their default assumption should be “intentionally heavy until proven otherwise,” not “overbroad by default.”
- Alternatives considered:
- Treat all surface-guard cost as excessive: rejected because these families intentionally protect cross-resource governance contracts.
- Exclude them from the plan entirely: rejected because they are still major contributors to lane cost and may need second-pass analysis.
## Decision 8: Use the existing heavy-governance report artifacts as the before-and-after evidence contract
- Decision: Pre-change and post-change evidence should continue to flow through `heavy-governance-latest.summary.md`, `heavy-governance-latest.budget.json`, and `heavy-governance-latest.report.json` under `apps/platform/storage/logs/test-lanes`.
- Rationale: The repository already reads and writes these artifacts. Extending the same contract keeps Spec 209 measurable without introducing a new artifact root or new tool surface.
- Alternatives considered:
- Add a second report directory specifically for Spec 209: rejected because the current lane artifact contract is already canonical.
- Depend only on terminal output: rejected because reviewers need checked-in, inspectable budget evidence.
## Decision 9: Keep author guidance repo-local and adjacent to the existing lane contract
- Decision: Spec 209 should place future heavy-family guidance in the existing test-governance seam, centered on `TestLaneManifest` semantics, guard expectations, and checked-in review guidance rather than creating a separate framework or documentation tree.
- Rationale: Authors and reviewers already need the manifest and guard seams to understand lane ownership. Keeping the guidance there avoids a new abstraction layer and keeps maintenance local to the existing contract.
- Alternatives considered:
- Create a new standalone documentation subsystem for heavy tests: rejected because the guidance is specific to the repository's existing lane contract.
- Leave guidance only in the spec artifacts: rejected because authors need a lasting checked-in hint near the implementation seam after the spec is complete.
## Decision 10: Success requires explicit recovery or explicit recalibration, not quiet tolerance
- Decision: The feature should end in exactly one of two explicit outcomes: the heavy-governance lane recovers within the authoritative threshold for the rollout, which starts at `300` seconds until normalization completes, or the repository documents a conscious recalibration once the honest lane composition and dominant residual costs are understood.
- Rationale: Spec 209 exists to stabilize the heavy lane before CI enforcement. A vague “improved but still heavy” outcome would not satisfy that purpose.
- Alternatives considered:
- Accept any measurable improvement as sufficient: rejected because the spec explicitly requires a budget decision.
- Hard-code recalibration in advance: rejected because the plan must first test whether real recovery is feasible from the dominant hotspot families.

View File

@ -0,0 +1,300 @@
title + explanation + exactly 1 CTA, and tables provide search/sort/filters for core dimensions.
# Feature Specification: Heavy Governance Lane Cost Reduction
**Feature Branch**: `209-heavy-governance-cost`
**Created**: 2026-04-17
**Status**: Draft
**Input**: User description: "Spec 209 - Heavy Governance Lane Cost Reduction"
## Spec Candidate Check *(mandatory — SPEC-GATE-001)*
- **Problem**: The Heavy Governance lane now carries the correct expensive governance families after Spec 208, but the lane itself remains above its documented budget and lacks a precise, shared explanation of which families dominate that cost and why.
- **Today's failure**: Confidence has been repaired, yet Heavy Governance is still too expensive, overbroad family boundaries hide duplicate discovery or validation work, and later CI budget enforcement would punish an honest but still unstable lane.
- **User-visible improvement**: Contributors and reviewers get a Heavy Governance lane whose dominant costs are visible, intentionally sliced, and either brought back under budget or consciously recalibrated with evidence.
- **Smallest enterprise-capable version**: Inventory the dominant heavy-governance families, decompose the top hotspots by trust type and duplicated work, refactor the most overbroad families, rerun the lane with before-and-after reporting, and publish concise author or reviewer guidance for future heavy tests.
- **Explicit non-goals**: No re-run of the lane-segmentation decisions from Spec 208, no general fixture-slimming program, no CI matrix rollout, no browser strategy work, and no blanket removal of legitimate governance coverage.
- **Permanent complexity imported**: A checked-in heavy-family inventory, cost-driver vocabulary, hotspot reporting discipline, budget-recovery decision record, and concise author or reviewer guidance for future heavy tests.
- **Why now**: Spec 208 made the heavy cost honest. Without tightening the Heavy Governance lane now, the next CI-enforcement phase would be built on a lane that is semantically correct but still not economically stable.
- **Why not local**: One-off optimizations cannot explain or control a lane-wide cost class. The repository needs a shared inventory, a shared slimming rule set, and an explicit budget decision that reviewers can evaluate consistently.
- **Approval class**: Cleanup
- **Red flags triggered**: Another governance taxonomy and the possibility of budget recalibration. Defense: the scope is intentionally narrow to repository test-lane cost control and does not introduce new product runtime truth, new product persistence, or new operator-facing surfaces.
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexität: 1 | Produktnähe: 1 | Wiederverwendung: 2 | **Gesamt: 10/12**
- **Decision**: approve
## Spec Scope Fields *(mandatory)*
- **Scope**: workspace
- **Primary Routes**: No end-user HTTP routes change. The affected surfaces are repository-owned Heavy Governance lane manifests, hotspot inventories, runtime reports, family-level governance tests, and contributor guidance.
- **Data Ownership**: Workspace-owned classification notes, hotspot evidence, lane-budget reporting, family refactoring rules, and reviewer guidance. No tenant-owned runtime records or new product data are introduced.
- **RBAC**: No end-user authorization behavior changes. The affected actors are repository contributors, reviewers, and maintainers who need an honest and governable Heavy Governance lane.
## Proportionality Review *(mandatory when structural complexity is introduced)*
- **New source of truth?**: no
- **New persisted entity/table/artifact?**: no new product persistence; only repository-owned inventory, reporting evidence, and guidance updates
- **New abstraction?**: yes, but limited to a repository-level inventory and decomposition model for dominant heavy-governance families
- **New enum/state/reason family?**: no product runtime state is added
- **New cross-domain UI framework/taxonomy?**: no new cross-domain product taxonomy; this work only sharpens the repository's heavy-governance cost vocabulary
- **Current operator problem**: Maintainers cannot tell which heavy-governance families are legitimately expensive, which are redundant, and which are simply overbroad, so the lane stays expensive without a stable correction path.
- **Existing structure is insufficient because**: Spec 208 fixed lane placement, but not family width. The current heavy families can still combine discovery, surface, workflow, and guard trust in ways that repeat work and make budget drift hard to control.
- **Narrowest correct implementation**: Inventory the dominant heavy families, analyze the top hotspots, refactor only the most overbroad families, rerun the lane, and end with an explicit budget-recovery or recalibration decision.
- **Ownership cost**: The team must maintain the hotspot inventory, the family-slimming rules, the before-and-after budget evidence, and the short guidance that keeps future heavy tests from regressing.
- **Alternative intentionally rejected**: Blindly removing assertions or moving families back into lighter lanes, because that would hide cost instead of explaining and controlling it.
- **Release truth**: Current-release repository truth that stabilizes the Heavy Governance lane before any CI budget enforcement is made binding.
## Problem Statement
Spec 208 resolved the earlier dishonesty in lane placement: heavy governance cost is now concentrated in the Heavy Governance lane instead of leaking into Confidence.
That exposed a new and separate problem: the Heavy Governance lane itself is still too expensive for its documented budget.
The cost drivers are now believed to be structural rather than classificatory:
- Some heavy-governance families are too broad and try to prove multiple trust types in one pass.
- Discovery, validation, render, or surface scans may be repeated inside the same family or across closely related families.
- Workflow-heavy and surface-heavy guards may be bundled together even when they should be separate test concerns.
- The most expensive families are now visible, but their internal cost drivers are not yet decomposed well enough to target the right fix.
- The lane is semantically honest but not yet economically stable enough for later CI budget enforcement.
If this remains unresolved, the repository reaches an unhealthy middle state: the right tests live in the right lane, but the lane is still too expensive to use as a dependable budgeted contract.
## Dependencies
- Depends on Spec 206 - Test Suite Governance & Performance Foundation for lane vocabulary, baseline reporting discipline, and budget governance.
- Depends on Spec 207 - Shared Test Fixture Slimming for the earlier reduction of default fixture cost.
- Depends on Spec 208 - Heavy Suite Segmentation for the corrected placement of heavy governance families into the Heavy Governance lane.
- Recommended before any CI matrix or runtime budget enforcement follow-up.
- Does not block ordinary feature delivery, provided new heavy-governance tests continue to follow the current lane-classification rules.
## Goals
- Bring the Heavy Governance lane back under its documented budget or end with an explicit and justified recalibration.
- Inventory the dominant heavy-governance families and their main hotspot files.
- Decompose the top heavy families by trust type, breadth, and duplicate work.
- Reduce redundant discovery, validation, render, or surface work inside the targeted hotspot families.
- Preserve the governance trust delivered by heavy families while making their cost class more controllable and understandable.
- Create a stable basis for later CI budget enforcement.
## Non-Goals
- Reopening the main lane-assignment decisions from Spec 208.
- Replacing the fixture-cost work from Spec 207.
- General CI wiring or hard-fail enforcement.
- Browser-lane optimization.
- Broad rollback of legitimate governance guards merely to improve runtime headlines.
- Primary optimization of Confidence or Fast Feedback beyond any indirect gains that happen naturally.
## Assumptions
- The current Heavy Governance lane already reflects the correct high-level family placement after Spec 208.
- Baseline reporting from the current heavy-governance run can be regenerated before and after this work.
- Some families will remain intentionally expensive, but their purpose and residual cost should still be explicit.
- Not every heavy hotspot will require file splitting; some can be recovered by centralizing repeated work or tightening family scope.
- If a dominant hotspot turns out to be mostly helper-driven or fixture-driven rather than family-breadth-driven, that cause will be recorded explicitly instead of being disguised as a family problem.
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Inventory Heavy Governance Hotspots (Priority: P1)
As a maintainer, I want the dominant Heavy Governance families and files explicitly inventoried so I can see which families are making the lane miss its budget and what kind of cost each family represents.
**Why this priority**: Budget recovery cannot be honest until the lane's dominant costs are visible and named.
**Independent Test**: Run the Heavy Governance lane reporting path, review the resulting inventory, and confirm that the dominant families have named purposes, hotspot files, and cost-driver classifications.
**Acceptance Scenarios**:
1. **Given** a current Heavy Governance baseline run, **When** the hotspot inventory is produced, **Then** the dominant families are listed with family name, purpose, hotspot files, and primary cost-driver classification.
2. **Given** a family is known to dominate the lane, **When** the inventory is reviewed, **Then** it is labeled as overbroad, redundant, discovery-heavy, workflow-heavy, surface-heavy, helper-driven, fixture-driven, intentionally heavy, or another explicitly explained cost type.
3. **Given** the lane is over budget, **When** a maintainer reads the inventory, **Then** they can identify which families are responsible for most of the overrun.
---
### User Story 2 - Slim Overbroad Heavy Families Without Losing Trust (Priority: P1)
As a maintainer, I want the most overbroad heavy families split or tightened so the lane stops repeating equivalent discovery or workflow work while keeping governance trust legible.
**Why this priority**: The largest gains are expected to come from fixing the breadth and duplication of the worst hotspot families, not from cosmetic file moves.
**Independent Test**: Refactor one of the top hotspot families, run its focused pack, and confirm that the resulting families have clearer trust boundaries, less repeated work, and preserved guard intent.
**Acceptance Scenarios**:
1. **Given** a heavy family mixes discovery, workflow, and surface trust, **When** it is refactored, **Then** the resulting tests have clearer semantic boundaries and no unnecessary catch-all scope.
2. **Given** duplicate discovery or validation work exists within a targeted family, **When** the family is slimmed, **Then** repeated passes are reduced or centralized without removing the intended governance guard.
3. **Given** a family remains legitimately heavy after refactoring, **When** it is reviewed, **Then** its remaining cost and trust purpose are explicitly documented.
---
### User Story 3 - Resolve Heavy Lane Budget Failure Explicitly (Priority: P2)
As a maintainer, I want the Heavy Governance lane rerun after the slimming pass so I can prove whether the lane is back within budget or whether the budget itself must be consciously recalibrated.
**Why this priority**: The feature is only complete when budget failure ends in a real decision rather than an unexplained acceptance of ongoing overrun.
**Independent Test**: Rerun the lane after the targeted family changes, compare the baseline and post-change results, and confirm that the outcome records either budget recovery or a justified recalibration decision.
**Acceptance Scenarios**:
1. **Given** the top hotspot families have been slimmed, **When** the Heavy Governance lane is rerun, **Then** the before-and-after delta and remaining hotspots are documented.
2. **Given** the lane still exceeds its former budget after real slimming, **When** the results are reviewed, **Then** the outcome records a conscious recalibration decision with evidence instead of silently treating the overrun as acceptable.
3. **Given** a touched heavy-governance family, **When** the post-change manifest and outcome are reviewed, **Then** the family remains in Heavy Governance unless an explicit non-budget rationale and spec-backed reclassification are recorded.
---
### User Story 4 - Guide Future Heavy Test Authors (Priority: P2)
As a reviewer or contributor, I want short guidance for Heavy Governance families so new heavy tests do not reintroduce the same overbroad patterns that made the lane unstable.
**Why this priority**: Without author guidance, the same structural mistakes can reappear immediately after the cleanup.
**Independent Test**: Review the guidance against a new or recently modified heavy-governance test and confirm that an author can decide whether the test belongs in an existing family, needs a new family, or should be split.
**Acceptance Scenarios**:
1. **Given** a new heavy-governance test is proposed, **When** the author guidance is applied, **Then** the reviewer can decide whether it belongs in an existing family or needs a separate family with explicit rationale.
2. **Given** a proposed test mixes discovery, surface, and workflow trust, **When** the guidance is applied, **Then** the reviewer can tell whether those concerns must be split to avoid another catch-all family.
### Edge Cases
- A hotspot family appears expensive, but the dominant cost is actually a shared helper or fixture path outside the family itself.
- Splitting a family creates clearer files but does not reduce runtime because the same central discovery work still runs repeatedly.
- A family is intentionally expensive and should remain in Heavy Governance, but it still needs an explicit explanation so reviewers do not mistake it for accidental bloat.
- The lane remains over budget even after duplicate work is removed, requiring a conscious recalibration rather than more arbitrary trimming.
- Multiple hotspot families depend on the same discovery or validation topology, so the correct optimization is centralization rather than repeated local edits.
## Requirements *(mandatory)*
**Constitution alignment:** This feature changes no end-user routes, no Microsoft Graph behavior, no queued operation semantics, no authorization planes, and no operator-facing product surfaces. It extends repository test-governance rules only, so the heavy-family inventory, slimming rules, hotspot evidence, and budget decision must remain explicit, reviewable, and measurable.
**Constitution alignment (PROP-001 / ABSTR-001 / BLOAT-001 / TEST-TRUTH-001):** The only new structure is a narrow repository-level inventory and decomposition model for heavy-governance hotspots. It is justified because the current over-budget lane cannot be corrected reliably through isolated local edits or by hiding cost in lighter lanes. The solution must stay explicit, avoid speculative frameworking, and preserve clear trust-to-family mapping.
**Budget authority for this rollout:** Until the normalization work is implemented, the Heavy Governance lane summary threshold of `300s` is the authoritative pre-normalization contract for recovery-or-recalibration decisions. The `200s` lane evaluation still emitted by `budgetTargets()` is legacy drift evidence that must remain visible until reconciled, but it is not a second passing threshold.
### Functional Requirements
- **FR-001 Heavy Family Inventory**: The repository MUST produce an explicit inventory of the dominant Heavy Governance families, including family name, primary purpose, primary hotspot files, and primary cost-driver classification.
- **FR-002 Known Hotspot Inclusion**: The inventory MUST include the current top 5 Heavy Governance families by lane time, or enough families to explain at least 80% of the lane's current runtime, whichever set is larger. This inclusion boundary MUST include baseline-profile-start-surfaces, findings-workflow-surfaces, and finding-bulk-actions-workflow while they remain inside that boundary, and MUST expand to any newly discovered family above the same boundary.
- **FR-003 Cost-Driver Classification**: Each inventoried family MUST be classified using a consistent vocabulary that distinguishes at least overbroad, redundant, discovery-heavy, workflow-heavy, surface-heavy, helper-driven, fixture-driven, and intentionally heavy causes.
- **FR-004 Internal Cost Decomposition**: The top hotspot families selected for action MUST each document which breadth is genuinely required, where duplicate discovery or validation work occurs, and which trust types are currently combined.
- **FR-005 No Catch-All Families**: A targeted Heavy Governance family MUST NOT remain a catch-all that mixes unrelated discovery, workflow, and surface trust without explicit documented justification.
- **FR-006 Duplicate Work Reduction**: Where targeted families repeat semantically equivalent discovery, validation, render, or surface-scanning work, that work MUST be reduced, centralized, or otherwise made non-duplicative.
- **FR-007 Guard Preservation**: Every refactored heavy family MUST retain a clear explanation of which governance rule or trust type it protects so that runtime gains do not come from hidden guard loss.
- **FR-008 Hotspot Reporting**: The Heavy Governance reporting output MUST show lane time before and after the slimming pass, top hotspot files or families, remaining open-expensive families, stabilized families, and whether the checked-in hotspot inventory satisfies the top-5-or-80%-of-runtime inclusion rule.
- **FR-009 Honest Budget Outcome**: After the targeted slimming pass, the Heavy Governance lane MUST be rerun and end with one of two explicit outcomes against the authoritative heavy-governance budget contract: documented recovery within the current threshold or a documented recalibration of the heavy-lane budget based on the now-correct lane composition.
- **FR-010 No Cost Hiding**: Heavy-governance families touched by this spec MUST retain Heavy Governance lane membership unless a non-budget rationale for reclassification is explicitly recorded and approved via a spec update. They MUST NOT be moved into lighter lanes solely to satisfy the budget target.
- **FR-011 Residual Cause Recording**: If a hotspot's dominant cost is determined to be primarily fixture-driven, helper-driven, or otherwise outside family scope, that residual cause MUST be recorded explicitly and routed as follow-up debt rather than being misreported as family-width improvement.
- **FR-012 Author And Reviewer Guidance**: The repository MUST provide concise guidance stating when a new heavy family is justified, when a test belongs in an existing heavy family, when a test is too broad, and when discovery, surface, and workflow concerns should be separated.
- **FR-013 Budget Contract Precedence**: For Spec 209 acceptance and reporting, the Heavy Governance lane summary threshold of `300s` is the authoritative pre-normalization budget contract until the normalization work publishes one reconciled threshold. The `200s` value still emitted by `budgetTargets()` MUST remain visible as legacy drift evidence but MUST NOT be interpreted as a second passing threshold.
### Non-Functional Requirements
- **NFR-001 Budget Honesty**: Heavy Governance cost must remain visible and attributable rather than being hidden through relabeling or silent exclusions.
- **NFR-002 Review Clarity**: A reviewer must be able to explain why a dominant family is heavy and what trust it delivers without relying on local tribal knowledge.
- **NFR-003 Incremental Slimming**: The highest-cost families must be reducible in targeted slices rather than requiring a full rewrite of the lane.
- **NFR-004 Stable Enforcement Readiness**: The resulting lane must be stable enough that later CI budget enforcement can treat its budget as a credible contract, meaning a heavy-governance rerun through the standard wrappers emits summary, budget, and report artifacts that expose the same authoritative threshold and the same budget outcome classification.
## Work Packages
### Work Package A - Heavy Hotspot Inventory
- Profile the current Heavy Governance lane.
- Identify the dominant families and hotspot files.
- Classify each dominant family by primary cost driver.
- Record the current heavy-lane baseline that the recovery pass will be measured against.
### Work Package B - Family Semantics Audit
- For each top hotspot family, identify which trust type it delivers.
- Separate required breadth from accidental breadth.
- Identify duplicate discovery, validation, or surface work.
- Distinguish true family-width issues from helper or fixture issues.
### Work Package C - Dominant Family Refactoring
- Split overbroad families into narrower units when needed.
- Centralize repeated discovery or validation work when that is the real hotspot.
- Separate workflow-heavy checks from surface-heavy or discovery-heavy guards where that improves both clarity and cost.
- Keep each resulting family's trust purpose explicit.
### Work Package D - Budget Recovery Validation
- Rerun the Heavy Governance lane after the slimming pass.
- Document the before-and-after lane delta and remaining hotspots.
- Decide explicitly whether the lane is back under budget or whether the budget must be recalibrated.
- Record any bounded residual debt that remains after honest slimming.
### Work Package E - Author And Reviewer Guidance
- Add short rules for when a heavy family is justified.
- Explain when a new test belongs in an existing heavy family.
- Explain when a proposed heavy test is too broad.
- Give reviewers a clear rule for splitting discovery, workflow, and surface trust when they are unnecessarily combined.
## Deliverables
- A checked-in inventory of dominant Heavy Governance families and hotspot files.
- A cost-driver classification for the heavy-lane hotspots.
- Slimmed or decomposed versions of the dominant overbroad families.
- Updated before-and-after heavy-lane reporting and budget evidence.
- An explicit budget outcome: recovery within the current threshold or a consciously documented recalibration.
- Short author or reviewer guidance for future Heavy Governance tests.
- A final summary of residual risks or remaining debt.
## Risks
### Governance Hollowing
Over-aggressive slimming could remove real governance trust instead of only removing redundant work.
### Cosmetic Decomposition
Renaming or splitting files without actually reducing duplicate work could make the lane look cleaner without recovering budget.
### Budget Gaming
Moving heavy cost into lighter lanes or redefining scope to satisfy the target would damage the credibility of the repository's lane governance.
### Misclassified Residual Cost
Some hotspots may ultimately be helper-driven or fixture-driven; if that is not recorded honestly, the family-level analysis will be misleading.
### Overfitting To Today's Hotspots
If the spec focuses only on today's top families and does not leave reviewer guidance behind, the next wave of heavy tests can recreate the same problem.
## Rollout Guidance
- Profile the current lane before making structural edits.
- Audit the top families semantically before trimming any assertions.
- Prefer removing duplicate work or clarifying family scope before deleting coverage.
- Rerun the lane after the targeted slimming pass and record the delta immediately.
- If the lane is still honestly over budget, end with a conscious recalibration or bounded follow-up debt instead of silent acceptance.
## Design Rules
- **Heavy stays honest**: Heavy cost must remain visible in the Heavy Governance lane.
- **Trust first, then trimming**: Protect the governance rule first, then remove accidental breadth or duplication.
- **No catch-all families**: A family should not mix unrelated trust types without explicit reason.
- **No duplicate discovery without cause**: Equivalent discovery or validation passes require a clear justification.
- **Budget failure resolves explicitly**: Over-budget status must end in recovery or a conscious recalibration decision.
- **Reviewer legibility is required**: A reviewer must be able to see why a family is heavy and what it protects.
### Key Entities *(include if feature involves data)*
- **Heavy Governance Family**: A named cluster of governance tests that contributes deliberate high-cost safety to the Heavy Governance lane.
- **Cost-Driver Classification**: The recorded explanation of why a heavy family is expensive, such as overbroad scope, duplicate work, discovery breadth, workflow breadth, or intentionally retained depth.
- **Hotspot Inventory**: The checked-in record of dominant heavy-lane families, their purpose, hotspot files, and primary cost drivers.
- **Budget Outcome Record**: The documented result that states whether the Heavy Governance lane recovered within budget or required a conscious recalibration.
## Success Criteria *(mandatory)*
### Measurable Outcomes
- **SC-001**: The hotspot inventory covers at least the top 5 Heavy Governance families by current lane time, or enough families to explain at least 80% of the lane's current runtime, whichever set is larger.
- **SC-002**: Every targeted hotspot family has a documented purpose, primary trust type, hotspot files, and primary cost-driver classification.
- **SC-003**: At least the top 3 targeted hotspot families are slimmed, decomposed, or explicitly retained as intentionally heavy with a documented reason.
- **SC-004**: After the slimming pass, the Heavy Governance lane either runs within the authoritative threshold defined for this rollout, which is `300s` until the normalization work publishes a reconciled threshold, or ends with a newly documented budget that includes evidence that the lane composition is now semantically correct and the remaining cost is legitimate.
- **SC-005**: The post-change reporting view exposes at least the top 10 Heavy Governance hotspots, confirms that the inventory covers the top 5 families or at least 80% of runtime, whichever is larger, and marks which targeted families improved, which remain open, and which were intentionally retained as heavy.
- **SC-006**: Reviewer guidance enables a contributor to classify a new heavy-governance test without undocumented local knowledge and to detect when a proposed test is too broad.
- **SC-007**: No targeted heavy-governance family is moved into Fast Feedback or Confidence solely for budget reasons during this rollout.

View File

@ -0,0 +1,182 @@
# Tasks: Heavy Governance Lane Cost Reduction
**Input**: Design documents from `/Users/ahmeddarrazi/Documents/projects/TenantAtlas/specs/209-heavy-governance-cost/`
**Prerequisites**: `plan.md` (required), `spec.md` (required), `research.md`, `data-model.md`, `contracts/`, `quickstart.md`
**Tests**: Required. This feature changes repository test-governance behavior, so each user story includes Pest guard coverage and focused validation through Sail.
**Organization**: Tasks are grouped by user story so each story can be implemented and validated independently.
## Phase 1: Setup (Shared Context)
**Purpose**: Refresh and confirm the current heavy-governance budget inputs, artifact contract, and hotspot families before changing shared seams.
- [X] T001 Refresh the current heavy-governance baseline by rerunning `scripts/platform-test-lane` and `scripts/platform-test-report` before family edits, then audit the refreshed artifacts in `apps/platform/storage/logs/test-lanes/heavy-governance-latest.summary.md`, `apps/platform/storage/logs/test-lanes/heavy-governance-latest.budget.json`, and `apps/platform/storage/logs/test-lanes/heavy-governance-latest.report.json`
- [X] T002 [P] Review the current heavy-lane seams in `apps/platform/tests/Support/TestLaneManifest.php`, `apps/platform/tests/Support/TestLaneBudget.php`, `apps/platform/tests/Support/TestLaneReport.php`, `scripts/platform-test-lane`, and `scripts/platform-test-report`
- [X] T003 [P] Review the primary hotspot families in `apps/platform/tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php`, `apps/platform/tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php`, `apps/platform/tests/Feature/Filament/BaselineActionAuthorizationTest.php`, `apps/platform/tests/Feature/Findings/FindingWorkflowRowActionsTest.php`, `apps/platform/tests/Feature/Findings/FindingWorkflowViewActionsTest.php`, and `apps/platform/tests/Feature/Findings/FindingBulkActionsTest.php`
---
## Phase 2: Foundational (Blocking Prerequisites)
**Purpose**: Add the shared manifest, budget, report, and wrapper seams that all user stories depend on.
**Critical**: No user story work should begin until this phase is complete.
- [X] T004 Extend `apps/platform/tests/Support/TestLaneManifest.php` with neutral accessors for the heavy-governance budget contract, hotspot inventory records, decomposition records, slimming decisions, and budget outcome placeholders aligned to `specs/209-heavy-governance-cost/contracts/heavy-governance-hotspot-inventory.schema.json`
- [X] T005 [P] Extend `apps/platform/tests/Support/TestLaneBudget.php` and `apps/platform/tests/Support/TestLaneReport.php` with neutral helpers for normalized heavy-governance thresholds, snapshot comparison, and explicit recovered or recalibrated outcomes
- [X] T006 [P] Update `apps/platform/tests/Feature/Guards/TestLaneManifestTest.php`, `apps/platform/tests/Feature/Guards/TestLaneArtifactsContractTest.php`, and `apps/platform/tests/Feature/Guards/TestLaneCommandContractTest.php` to accept the expanded heavy-governance inventory, snapshot, and budget outcome metadata shape
- [X] T007 [P] Update `scripts/platform-test-lane` and `scripts/platform-test-report` so heavy-governance baseline and rerun artifacts can be refreshed consistently under `apps/platform/storage/logs/test-lanes`
**Checkpoint**: Shared heavy-governance seams are ready for story-specific implementation.
---
## Phase 3: User Story 1 - Inventory Heavy Governance Hotspots (Priority: P1)
**Goal**: Seed a checked-in hotspot inventory that names the dominant heavy-governance families, their measured cost, and the authoritative pre-normalization budget contract plus legacy drift signal that must be normalized.
**Independent Test**: Run the heavy-governance report path and confirm the manifest and artifacts expose the dominant families, their cost-driver categories, and the current budget signals.
### Tests for User Story 1
- [X] T008 [P] [US1] Expand `apps/platform/tests/Feature/Guards/HeavyGovernanceLaneContractTest.php` to assert the top heavy-governance families, priority tiers, the authoritative pre-normalization `300s` threshold, the visible legacy `200s` drift signal, and lane-membership invariants for touched families are exposed from `apps/platform/tests/Support/TestLaneManifest.php`
- [X] T009 [P] [US1] Expand `apps/platform/tests/Feature/Guards/ProfileLaneContractTest.php` and `apps/platform/tests/Feature/Guards/TestLaneArtifactsContractTest.php` to assert heavy-governance reports surface top 10 hotspots, family totals, classification totals, inventory coverage for the top 5 families or at least 80% of runtime, whichever is larger, and snapshot artifact paths under `apps/platform/storage/logs/test-lanes`
### Implementation for User Story 1
- [X] T010 [US1] Populate the canonical hotspot inventory and current heavy-governance budget contract in `apps/platform/tests/Support/TestLaneManifest.php` for the current top 5 heavy families by runtime, or enough families to explain at least 80% of runtime, whichever set is larger, including `baseline-profile-start-surfaces`, `findings-workflow-surfaces`, `finding-bulk-actions-workflow`, `action-surface-contract`, `ops-ux-governance`, and `workspace-settings-slice-management`
- [X] T011 [US1] Extend `apps/platform/tests/Support/TestLaneReport.php` to emit coverage-oriented heavy-governance snapshot data that explains the top 5 families or at least 80% of lane runtime, whichever is larger, and reports whether the inclusion rule is satisfied
- [X] T012 [US1] Align the measured hotspot baseline and reviewer-facing inventory summary in `specs/209-heavy-governance-cost/quickstart.md` with the canonical inventory stored in `apps/platform/tests/Support/TestLaneManifest.php`, including the authoritative pre-normalization `300s` threshold and the legacy `200s` drift signal
**Checkpoint**: User Story 1 is complete when the dominant heavy-governance hotspots are reviewable and contract-tested without yet changing the family internals.
---
## Phase 4: User Story 2 - Slim Overbroad Heavy Families Without Losing Trust (Priority: P1)
**Goal**: Reduce duplicated work and accidental breadth in the dominant heavy families while preserving their governance guarantees.
**Independent Test**: Refactor the targeted hotspot families, run the focused packs, and confirm the resulting families keep their guard intent while shedding repeated work.
### Tests for User Story 2
- [X] T013 [P] [US2] Expand focused regression coverage in `apps/platform/tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php`, `apps/platform/tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php`, and `apps/platform/tests/Feature/Filament/BaselineActionAuthorizationTest.php` to lock baseline start-surface guard behavior before slimming
- [X] T014 [P] [US2] Expand focused regression coverage in `apps/platform/tests/Feature/Findings/FindingBulkActionsTest.php`, `apps/platform/tests/Feature/Findings/FindingWorkflowRowActionsTest.php`, `apps/platform/tests/Feature/Findings/FindingWorkflowViewActionsTest.php`, `apps/platform/tests/Feature/Findings/FindingsListFiltersTest.php`, and `apps/platform/tests/Feature/Findings/FindingExceptionRenewalTest.php` to lock workflow, filter, and audit behavior before decomposition
- [X] T015 [P] [US2] Expand `apps/platform/tests/Feature/Guards/ActionSurfaceContractTest.php` and `apps/platform/tests/Feature/Guards/OperationLifecycleOpsUxGuardTest.php` to preserve current governance invariants if second-wave surface-guard slimming becomes necessary
### Implementation for User Story 2
- [X] T016 [US2] Decompose the `baseline-profile-start-surfaces` family across `apps/platform/tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php`, `apps/platform/tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php`, and `apps/platform/tests/Feature/Filament/BaselineActionAuthorizationTest.php` to remove repeated Livewire mounts and gating matrices while preserving authorization and rollout guards
- [X] T017 [P] [US2] Slim the `findings-workflow-surfaces` family in `apps/platform/tests/Feature/Findings/FindingWorkflowRowActionsTest.php`, `apps/platform/tests/Feature/Findings/FindingWorkflowViewActionsTest.php`, `apps/platform/tests/Feature/Findings/FindingsListFiltersTest.php`, and `apps/platform/tests/Feature/Findings/FindingExceptionRenewalTest.php` by separating required workflow trust from repeated surface or filter work
- [X] T018 [P] [US2] Slim the `finding-bulk-actions-workflow` family in `apps/platform/tests/Feature/Findings/FindingBulkActionsTest.php` to remove duplicated bulk-action setup and audit fan-out without weakening per-record workflow guarantees
- [X] T019 [US2] Record decomposition results, residual causes, and slimming decisions for the workflow-heavy hotspot families in `apps/platform/tests/Support/TestLaneManifest.php`
- [X] T020 [US2] Audit `apps/platform/tests/Feature/Guards/ActionSurfaceContractTest.php`, `apps/platform/tests/Feature/Guards/OperationLifecycleOpsUxGuardTest.php`, `apps/platform/tests/Feature/OpsUx/OperationCatalogCoverageTest.php`, `apps/platform/tests/Feature/OpsUx/OperateHubShellTest.php`, and `apps/platform/tests/Feature/OpsUx/ActiveRunsTest.php` to either identify removable duplication or explicitly retain them as intentionally heavy in `apps/platform/tests/Support/TestLaneManifest.php`
**Checkpoint**: User Story 2 is complete when the primary workflow-heavy hotspot families are slimmer, their trust boundaries are explicit, and any intentionally retained heavy families are documented as such.
---
## Phase 5: User Story 3 - Resolve Heavy Lane Budget Failure Explicitly (Priority: P2)
**Goal**: Normalize the heavy-governance budget contract, rerun the lane, and end with an explicit recovered or recalibrated outcome.
**Independent Test**: Rerun the heavy-governance lane after the slimming pass and confirm the updated artifacts expose one authoritative threshold, before-and-after deltas, and an explicit budget decision.
### Tests for User Story 3
- [X] T021 [P] [US3] Expand `apps/platform/tests/Feature/Guards/HeavyGovernanceLaneContractTest.php` and `apps/platform/tests/Feature/Guards/FixtureLaneImpactBudgetTest.php` to assert one authoritative heavy-governance threshold, explicit budget decision status, and remaining-open-family reporting
- [X] T022 [P] [US3] Expand `apps/platform/tests/Feature/Guards/ProfileLaneContractTest.php` and `apps/platform/tests/Feature/Guards/TestLaneArtifactsContractTest.php` to assert baseline and post-change heavy-governance snapshots plus final budget outcome artifacts under `apps/platform/storage/logs/test-lanes`, including non-conflicting threshold and outcome values across summary, budget, and report outputs
### Implementation for User Story 3
- [X] T023 [US3] Normalize the heavy-governance budget contract in `apps/platform/tests/Support/TestLaneBudget.php` and `apps/platform/tests/Support/TestLaneManifest.php` so the authoritative pre-normalization `300s` summary threshold and the legacy `200s` detailed budget evaluation resolve to one authoritative threshold and lifecycle state
- [X] T024 [US3] Extend `apps/platform/tests/Support/TestLaneReport.php` to emit baseline or post-change deltas, remaining open families, and explicit `recovered` or `recalibrated` budget outcome records
- [X] T025 [US3] Audit `apps/platform/tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php` as the first fallback ui-workflow hotspot and either narrow duplicated work or record it as retained residual cost in `apps/platform/tests/Support/TestLaneManifest.php`
- [X] T026 [US3] Update `scripts/platform-test-lane` and `scripts/platform-test-report` so heavy-governance baseline capture and rerun reporting produce consistent summary, budget, and report artifacts for the final decision
- [X] T027 [US3] Refresh `apps/platform/storage/logs/test-lanes/heavy-governance-latest.summary.md`, `apps/platform/storage/logs/test-lanes/heavy-governance-latest.budget.json`, and `apps/platform/storage/logs/test-lanes/heavy-governance-latest.report.json` by rerunning the heavy-governance lane after the slimming pass and confirming the refreshed artifacts agree on the final threshold and budget outcome classification
- [X] T028 [US3] Record the final heavy-governance recovery or recalibration rationale in `apps/platform/tests/Support/TestLaneManifest.php` and `specs/209-heavy-governance-cost/quickstart.md`
**Checkpoint**: User Story 3 is complete when the heavy-governance lane exposes one authoritative budget contract and a final explicit budget decision backed by refreshed artifacts.
---
## Phase 6: User Story 4 - Guide Future Heavy Test Authors (Priority: P2)
**Goal**: Leave behind short, enforceable rules that stop new heavy-governance tests from recreating the same overbroad patterns.
**Independent Test**: Review the manifest guidance and taxonomy guards to confirm a reviewer can decide whether a new heavy test belongs in an existing family, needs a new family, or must be split.
### Tests for User Story 4
- [X] T029 [P] [US4] Expand `apps/platform/tests/Feature/Guards/TestLaneManifestTest.php` to assert the heavy author-guidance rules cover new-family creation, family reuse, overbroad tests, and separation of discovery, workflow, and surface trust
- [X] T030 [P] [US4] Expand `apps/platform/tests/Feature/Guards/TestTaxonomyPlacementGuardTest.php` and `apps/platform/tests/Feature/Guards/HeavyGovernanceLaneContractTest.php` to require a valid family or guidance-backed classification decision for future heavy-governance additions
### Implementation for User Story 4
- [X] T031 [US4] Add canonical heavy-author guidance rules and anti-pattern signals to `apps/platform/tests/Support/TestLaneManifest.php`
- [X] T032 [US4] Publish reviewer-facing heavy-family guidance and residual-debt rules in `specs/209-heavy-governance-cost/quickstart.md` and keep the canonical wording synchronized with `apps/platform/tests/Support/TestLaneManifest.php`
**Checkpoint**: User Story 4 is complete when future heavy-governance additions have explicit guidance and guard-backed review rules.
---
## Phase 7: Polish & Cross-Cutting Concerns
**Purpose**: Validate the end-to-end rollout, format the code, and remove superseded notes after the heavy-lane decision is settled.
- [X] T033 Run focused Pest coverage for `apps/platform/tests/Feature/Guards/HeavyGovernanceLaneContractTest.php`, `apps/platform/tests/Feature/Filament/BaselineProfileCaptureStartSurfaceTest.php`, `apps/platform/tests/Feature/Filament/BaselineProfileCompareStartSurfaceTest.php`, `apps/platform/tests/Feature/Filament/BaselineActionAuthorizationTest.php`, `apps/platform/tests/Feature/Findings/FindingBulkActionsTest.php`, `apps/platform/tests/Feature/Findings/FindingWorkflowRowActionsTest.php`, `apps/platform/tests/Feature/Findings/FindingWorkflowViewActionsTest.php`, `apps/platform/tests/Feature/Findings/FindingsListFiltersTest.php`, `apps/platform/tests/Feature/Findings/FindingExceptionRenewalTest.php`, and `apps/platform/tests/Feature/SettingsFoundation/WorkspaceSettingsManageTest.php` with `cd apps/platform && ./vendor/bin/sail artisan test --compact ...`
- [X] T034 Run heavy-governance wrapper validation via `scripts/platform-test-lane`, `scripts/platform-test-report`, and inspect `apps/platform/storage/logs/test-lanes/heavy-governance-latest.summary.md`, `apps/platform/storage/logs/test-lanes/heavy-governance-latest.budget.json`, and `apps/platform/storage/logs/test-lanes/heavy-governance-latest.report.json` to confirm the final budget outcome and a non-conflicting threshold or outcome contract across all three artifacts
- [X] T035 Run formatting for `apps/platform/` with `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- [X] T036 Remove stale dual-threshold comments, superseded family notes, and obsolete draft placeholders in `apps/platform/tests/Support/TestLaneManifest.php`, `apps/platform/tests/Support/TestLaneBudget.php`, `apps/platform/tests/Support/TestLaneReport.php`, and `specs/209-heavy-governance-cost/quickstart.md`
---
## Dependencies
- Phase 1 must complete before Phase 2.
- Phase 2 must complete before any user story work begins.
- User Story 1 is the MVP and must complete before User Story 2, User Story 3, or User Story 4.
- User Story 2 depends on User Story 1 because slimming decisions require the canonical hotspot inventory and decomposition scaffolding.
- User Story 3 depends on User Story 1 and User Story 2 because the explicit budget decision depends on the updated inventory and the slimming pass.
- User Story 4 depends on User Story 1 and User Story 3 because the final guidance should reflect the authoritative family model and the settled budget contract.
- Phase 7 depends on all user stories.
## Parallel Execution Examples
### User Story 1
- Run T008 and T009 in parallel to lock the inventory and artifact-report contracts.
- After T010 lands, T011 and T012 can proceed in parallel across report output and reviewer-facing guidance.
### User Story 2
- Run T013, T014, and T015 in parallel because they lock separate hotspot families before slimming.
- After T016 lands, T017 and T018 can run in parallel across findings workflow and bulk-action families.
### User Story 3
- Run T021 and T022 in parallel because they cover budget-contract and artifact-outcome guards separately.
- After T023 lands, T024 and T026 can run in parallel across report generation and wrapper reporting.
### User Story 4
- Run T029 and T030 in parallel because they enforce different review surfaces.
- After T031 lands, T032 can publish the final wording in the quickstart guidance.
## Implementation Strategy
### MVP First
- Deliver User Story 1 first to establish the canonical hotspot inventory and the current heavy-governance budget signal.
- Deliver User Story 2 next to produce the first real runtime recovery by narrowing the dominant workflow-heavy families.
### Incremental Delivery
- Add User Story 3 after the primary slimming pass so the budget decision reflects real runtime changes rather than a draft threshold.
- Add User Story 4 last so the guidance codifies the final family model and budget contract.
### Validation Sequence
- Use focused hotspot tests first, then guard suites, then heavy-governance wrapper reruns, and only then refresh formatting and stale-note cleanup.
- Treat `apps/platform/storage/logs/test-lanes/` as the canonical artifact root throughout validation and budget review.