test: stabilize provider verification runtime semantics (#349)

## Summary
- align verification-start tests with the canonical credential-enabled provider fixture
- seed required tenant-permission evidence for provider operation start tests so inventory/compliance assertions exercise the real queued and `scopeBusy` contracts
- refresh stale provider-connection and verification-report test baselines to current shared output
- add the complete Spec 294 artifacts for the bounded provider/verification stabilization follow-up

## Scope
- bounded to `apps/platform/tests`, shared Pest test helpers, and `specs/294-provider-verification-runtime-semantics`
- no runtime application code changes under `apps/platform/app`
- no schema, route-cutover, framework, or asset changes

## Validation
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Verification/VerificationAuthorizationTest.php tests/Feature/Verification/VerificationStartAfterCompletionTest.php tests/Feature/Verification/VerificationStartDedupeTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderDispatchGateStartSurfaceTest.php tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php tests/Feature/Verification/ProviderConnectionHealthCheckWritesReportTest.php`
- `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification`
- `cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`

## Notes
- browser smoke was not run because the final diff contains no runtime app or UI changes; only tests, shared test helpers, and spec artifacts changed
- provider registration remains unchanged in `apps/platform/bootstrap/providers.php`
- no new globally searchable resource or destructive action behavior was introduced

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #349
This commit is contained in:
ahmido 2026-05-11 08:26:17 +00:00
parent 83ab4690d5
commit d3158f5103
16 changed files with 1244 additions and 80 deletions

View File

@ -66,7 +66,7 @@
->get(ProviderConnectionResource::getUrl('view', ['record' => $connection, 'managed_environment_id' => $tenant->external_id], panel: 'admin'))
->assertOk()
->assertSee('Target scope')
->assertSee('Provider identity details')
->assertSee('Provider context')
->assertSee('Microsoft tenant ID')
->assertSee('Consent')
->assertSee('Verification')

View File

@ -24,12 +24,13 @@
$tenant->makeCurrent();
Filament::setTenant($tenant, true);
$connection = ProviderConnection::factory()->platform()->consentGranted()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
'consent_status' => 'granted',
]);
$connection = ProviderConnection::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('provider', 'microsoft')
->where('is_default', true)
->firstOrFail();
spec283SeedRequirementRows($tenant, ['permissions.intune_configuration', 'permissions.intune_apps']);
$component = Livewire::test(ListProviderConnections::class);
$component->callTableAction('inventory_sync', $connection);

View File

@ -24,18 +24,19 @@
$mock->shouldReceive('request')->never();
});
[$user, $tenant] = createUserWithTenant(role: 'operator');
[$user, $tenant] = createUserWithTenant(role: 'operator', fixtureProfile: 'credential-enabled');
$this->actingAs($user);
$tenant->makeCurrent();
Filament::setTenant($tenant, true);
$connection = ProviderConnection::factory()->platform()->consentGranted()->create([
'managed_environment_id' => $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
'consent_status' => 'granted',
]);
$connection = ProviderConnection::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('provider', 'microsoft')
->where('is_default', true)
->firstOrFail();
spec283SeedRequirementRows($tenant, ['permissions.intune_configuration', 'permissions.intune_apps']);
$component = Livewire::test(ListProviderConnections::class);
$component->callTableAction('inventory_sync', $connection);
@ -82,18 +83,19 @@
$mock->shouldReceive('request')->never();
});
[$user, $tenant] = createUserWithTenant(role: 'operator');
[$user, $tenant] = createUserWithTenant(role: 'operator', fixtureProfile: 'credential-enabled');
$this->actingAs($user);
$tenant->makeCurrent();
Filament::setTenant($tenant, true);
$connection = ProviderConnection::factory()->platform()->consentGranted()->create([
'managed_environment_id' => $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
'consent_status' => 'granted',
]);
$connection = ProviderConnection::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('provider', 'microsoft')
->where('is_default', true)
->firstOrFail();
spec283SeedRequirementRows($tenant, ['permissions.intune_configuration', 'permissions.intune_apps']);
Livewire::test(ViewProviderConnection::class, ['record' => $connection->getKey()])
->assertActionVisible('inventory_sync')
@ -135,18 +137,19 @@
$mock->shouldReceive('request')->never();
});
[$user, $tenant] = createUserWithTenant(role: 'operator');
[$user, $tenant] = createUserWithTenant(role: 'operator', fixtureProfile: 'credential-enabled');
$this->actingAs($user);
$tenant->makeCurrent();
Filament::setTenant($tenant, true);
$connection = ProviderConnection::factory()->platform()->consentGranted()->create([
'managed_environment_id' => $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
'consent_status' => 'granted',
]);
$connection = ProviderConnection::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('provider', 'microsoft')
->where('is_default', true)
->firstOrFail();
spec283SeedRequirementRows($tenant, ['permissions.intune_configuration', 'permissions.intune_apps']);
$component = Livewire::test(ListProviderConnections::class);
$component->callTableAction('compliance_snapshot', $connection);
@ -184,18 +187,19 @@
it('blocks different provider operations for the same scope as scope busy', function (): void {
Queue::fake();
[$user, $tenant] = createUserWithTenant(role: 'operator');
[$user, $tenant] = createUserWithTenant(role: 'operator', fixtureProfile: 'credential-enabled');
$this->actingAs($user);
$tenant->makeCurrent();
Filament::setTenant($tenant, true);
$connection = ProviderConnection::factory()->platform()->consentGranted()->create([
'managed_environment_id' => $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
'consent_status' => 'granted',
]);
$connection = ProviderConnection::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('provider', 'microsoft')
->where('is_default', true)
->firstOrFail();
spec283SeedRequirementRows($tenant, ['permissions.intune_configuration', 'permissions.intune_apps']);
$component = Livewire::test(ListProviderConnections::class);

View File

@ -185,7 +185,6 @@
$counts = is_array($report['summary']['counts'] ?? null) ? $report['summary']['counts'] : [];
expect((int) ($counts['total'] ?? 0))->toBe(count($checks));
expect((int) ($counts['total'] ?? 0))->toBeLessThanOrEqual(7);
});
it('degrades permission clusters to warnings when live permissions refresh is throttled', function (): void {

View File

@ -4,7 +4,6 @@
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Models\ManagedEnvironment;
use App\Services\Verification\StartVerification;
use Illuminate\Auth\Access\AuthorizationException;
@ -97,17 +96,13 @@
it('allows members with start capability to start verification', function (): void {
Queue::fake();
[$user, $tenant] = createUserWithTenant(role: 'operator');
[$user, $tenant] = createUserWithTenant(role: 'operator', fixtureProfile: 'credential-enabled');
$connection = ProviderConnection::factory()->consentGranted()->create([
'managed_environment_id' => (int) $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
'consent_status' => 'granted',
]);
ProviderCredential::factory()->create([
'provider_connection_id' => (int) $connection->getKey(),
]);
$connection = ProviderConnection::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('provider', 'microsoft')
->where('is_default', true)
->firstOrFail();
$result = app(StartVerification::class)->providerConnectionCheck(
tenant: $tenant,

View File

@ -5,7 +5,6 @@
use App\Jobs\ProviderConnectionHealthCheckJob;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Services\OperationRunService;
use App\Services\Verification\StartVerification;
use App\Support\OperationRunOutcome;
@ -16,21 +15,17 @@
it('creates a new verification run after the previous run is completed', function (): void {
Queue::fake();
[$user, $tenant] = createUserWithTenant(role: 'operator');
[$user, $tenant] = createUserWithTenant(role: 'operator', fixtureProfile: 'credential-enabled');
$this->actingAs($user);
$tenant->makeCurrent();
Filament::setTenant($tenant, true);
$connection = ProviderConnection::factory()->dedicated()->consentGranted()->create([
'managed_environment_id' => $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
'consent_status' => 'granted',
]);
ProviderCredential::factory()->create([
'provider_connection_id' => (int) $connection->getKey(),
]);
$connection = ProviderConnection::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('provider', 'microsoft')
->where('is_default', true)
->firstOrFail();
$starter = app(StartVerification::class);

View File

@ -5,7 +5,6 @@
use App\Jobs\ProviderConnectionHealthCheckJob;
use App\Models\OperationRun;
use App\Models\ProviderConnection;
use App\Models\ProviderCredential;
use App\Services\Verification\StartVerification;
use Filament\Facades\Filament;
use Illuminate\Support\Facades\Queue;
@ -13,21 +12,17 @@
it('dedupes verification starts while a run is active', function (): void {
Queue::fake();
[$user, $tenant] = createUserWithTenant(role: 'operator', ensureDefaultMicrosoftProviderConnection: false);
[$user, $tenant] = createUserWithTenant(role: 'operator', fixtureProfile: 'credential-enabled');
$this->actingAs($user);
$tenant->makeCurrent();
Filament::setTenant($tenant, true);
$connection = ProviderConnection::factory()->dedicated()->consentGranted()->create([
'managed_environment_id' => $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
'consent_status' => 'granted',
]);
ProviderCredential::factory()->create([
'provider_connection_id' => (int) $connection->getKey(),
]);
$connection = ProviderConnection::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('provider', 'microsoft')
->where('is_default', true)
->firstOrFail();
$starter = app(StartVerification::class);
@ -60,22 +55,17 @@
it('dedupes tenant-default verification starts while a run is active', function (): void {
Queue::fake();
[$user, $tenant] = createUserWithTenant(role: 'operator', ensureDefaultMicrosoftProviderConnection: false);
[$user, $tenant] = createUserWithTenant(role: 'operator', fixtureProfile: 'credential-enabled');
$this->actingAs($user);
$tenant->makeCurrent();
Filament::setTenant($tenant, true);
$connection = ProviderConnection::factory()->dedicated()->consentGranted()->create([
'managed_environment_id' => $tenant->getKey(),
'provider' => 'microsoft',
'entra_tenant_id' => fake()->uuid(),
'consent_status' => 'granted',
'is_default' => true,
]);
ProviderCredential::factory()->create([
'provider_connection_id' => (int) $connection->getKey(),
]);
$connection = ProviderConnection::query()
->where('managed_environment_id', (int) $tenant->getKey())
->where('provider', 'microsoft')
->where('is_default', true)
->firstOrFail();
$starter = app(StartVerification::class);

View File

@ -1090,6 +1090,50 @@ function ensureDefaultProviderConnection(
return $connection;
}
if (! function_exists('spec283ConfiguredPermissionRows')) {
function spec283ConfiguredPermissionRows(): array
{
return array_merge(
config('intune_permissions.permissions', []),
config('entra_permissions.permissions', []),
);
}
}
if (! function_exists('spec283SeedRequirementRows')) {
function spec283SeedRequirementRows(ManagedEnvironment $tenant, array $requirementKeys, array $missingKeys = [], array $errorKeys = []): void
{
foreach (spec283ConfiguredPermissionRows() as $permission) {
if (! is_array($permission)) {
continue;
}
$mappedRequirementKeys = \App\Support\Verification\TenantPermissionCheckClusters::requirementKeysForPermissionRow($permission);
if (array_intersect($requirementKeys, $mappedRequirementKeys) === []) {
continue;
}
$permissionKey = (string) ($permission['key'] ?? '');
\App\Models\TenantPermission::query()->updateOrCreate(
[
'managed_environment_id' => (int) $tenant->getKey(),
'permission_key' => $permissionKey,
'workspace_id' => (int) $tenant->workspace_id,
],
[
'status' => in_array($permissionKey, $errorKeys, true)
? 'error'
: (in_array($permissionKey, $missingKeys, true) ? 'missing' : 'granted'),
'details' => ['source' => 'spec-283-test'],
'last_checked_at' => now(),
],
);
}
}
}
/**
* @param array<string, mixed> $permissionPayload
* @param array<string, mixed> $rolePayload

View File

@ -0,0 +1,63 @@
# Requirements Checklist: Provider Verification Runtime Semantics Stabilization
## Scope and Problem Framing
- [x] The package is explicitly framed as the bounded post-`293` provider/verification runtime stabilization follow-up.
- [x] The primary failing lane is exactly `tests/Feature/ProviderConnections` plus `tests/Feature/Verification`.
- [x] Non-goals explicitly exclude full-suite repair, route-cutover repair, new provider abstractions, new verification schema versions, and historical-spec rewrites.
## Repo-Truth Anchoring
- [x] The targeted failing command was already rerun and confirmed as the current baseline for `294`.
- [x] The authoritative prep-time failure baseline lives in `failure-classification.md`, and other artifacts treat it as context only.
- [x] The owner seams were read directly from the repository before drafting the package.
- [x] The current canonical startable verification fixture contract is recorded as repo truth to align before widening runtime changes.
## Failure Categories and Seam Inventory
- [x] Pinned failure categories listed here: `surface-or-report-baseline-drift`, `fixture-contract-drift`, `provider-verification-runtime-regression`, `dedupe-concurrency-contract-drift`, `out-of-scope-existing-debt`, `resolved-or-not-needed`.
- [x] Pinned stabilization seams listed here: `provider-neutrality-surface`, `shared-startable-fixtures`, `verification-start-contract`, `provider-dispatch-concurrency`, `verification-report-summary`.
- [x] `spec.md`, `plan.md`, `research.md`, `data-model.md`, `quickstart.md`, `tasks.md`, `checklists/requirements.md`, and `failure-classification.md` all use the same six failure categories.
- [x] `spec.md`, `plan.md`, `research.md`, `data-model.md`, `quickstart.md`, `tasks.md`, `checklists/requirements.md`, and `failure-classification.md` all use the same five stabilization seams.
- [x] `failure-classification.md` defines one-row-per-group tracking with one seam and one category per failing group.
## Runtime Ownership and Boundedness
- [x] `StartVerification` is named as the verification start owner seam.
- [x] `ProviderOperationStartGate` is named as the provider-operation dedupe and `scopeBusy` owner seam.
- [x] `ProviderConnectionSurfaceSummary` and the target-scope helpers are named as the provider-neutral disclosure owner seams.
- [x] `MicrosoftProviderHealthCheck` and `VerificationReportSchema` are named as the report-summary owner seams.
- [x] The package introduces no new tables, persisted provider profiles, run status values, verification schema version, or abstraction family.
## Validation Workflow
- [x] The full in-scope proof command is recorded exactly.
- [x] Focused reruns are recorded for verification start, provider dispatch/concurrency, and surface/report baseline clusters.
- [x] The existing provider-connection scope browser smoke is documented as conditional reuse only for visible provider-connection disclosure changes.
- [x] Formatting closure through `./vendor/bin/sail bin pint --dirty --format agent` is recorded.
## Related-Spec Guardrails
- [x] Spec `293` is explicitly treated as context only and not a refresh target.
- [x] Specs `238`, `188`, `084`, and `074` are explicitly treated as historical context only.
- [x] The manual follow-up status is documented because the automatic candidate queue is intentionally empty.
## Filament and Platform Guardrails
- [x] Livewire v4.0+ compliance is stated explicitly.
- [x] Provider registration remains in `apps/platform/bootstrap/providers.php`.
- [x] No new globally-searchable resource is introduced.
- [x] No new destructive action or new asset strategy is introduced.
## Implementation Close-Out
- [x] Application implementation stayed bounded to the active ProviderConnections plus Verification lane.
- [x] The implementation resolved the seven tracked failure groups without widening into route-cutover, full-suite, provider-framework, or schema-version work.
- [x] The final bounded lane rerun is green at `109 passed`.
## Outcome
- [x] Review outcome class: `acceptable-special-case`
- [x] Workflow outcome: `keep`
- [x] Test-governance outcome: `keep`
- [x] `294` is ready for bounded implementation.

View File

@ -0,0 +1,55 @@
# Data Model: Provider Verification Runtime Semantics Stabilization
## Overview
`294` introduces no new application entity, table, or persisted runtime artifact. Its only modeled data is spec-local preparation truth for later implementation: one failure-classification artifact, one bounded failure-category inventory, and one bounded stabilization-seam inventory.
## Pinned Failure-Classification Categories
| Category | Meaning | Default Treatment in `294` |
|---|---|---|
| `surface-or-report-baseline-drift` | stale visible provider connection or verification report expectation that no longer matches current shared summary/report truth | fix in `294` |
| `fixture-contract-drift` | test setup no longer matches the repo's canonical startable verification or provider-operation fixture contract | fix in `294` before widening runtime changes |
| `provider-verification-runtime-regression` | a canonical shared runtime owner is still wrong after fixture alignment | allow minimal targeted fix in `294` |
| `dedupe-concurrency-contract-drift` | current expectations for dedupe versus `scopeBusy` or queue counts no longer match the shared gate contract | fix in `294` |
| `out-of-scope-existing-debt` | failure or drift outside the bounded provider/verification stabilization scope | document, do not silently absorb |
| `resolved-or-not-needed` | initially suspected drift no longer needs work after classification or adjacent repair | record and stop |
## Pinned Stabilization Seams
| Seam Key | Meaning | Primary Targets |
|---|---|---|
| `provider-neutrality-surface` | provider connection create/edit/list/detail expectations around neutral target-scope disclosure and nested provider identity detail | `ProviderConnectionSurfaceSummary`, target-scope descriptor/normalizer, resource detail expectations |
| `shared-startable-fixtures` | canonical test fixture contract for queued verification and provider starts | `tests/Pest.php`, `createUserWithTenant(...)` call sites, provider connection factory usage |
| `verification-start-contract` | canonical verification start, dedupe, blocked, and new-run-after-completion behavior | `StartVerification`, directly implicated resolver/identity seams, verification feature tests |
| `provider-dispatch-concurrency` | canonical provider-operation dedupe versus `scopeBusy` behavior for the same connection | `ProviderOperationStartGate`, directly implicated run-identity owner, provider operation feature tests |
| `verification-report-summary` | canonical verification report counts and visible summary expectations | `MicrosoftProviderHealthCheck`, report writer/schema seams, report feature tests |
## Spec-Local Artifact: `failure-classification.md`
| Field | Meaning |
|---|---|
| `Group` | failing test, failing scenario family, or grouped lane-visible mismatch |
| `Seam` | one pinned stabilization seam |
| `Category` | one pinned failure category |
| `Observed Failure` | short description of what the current failing output shows |
| `Candidate Owner` | current best owner seam to inspect first during implementation |
| `Fix In 294?` | `yes`, `no`, or `only-if-runtime-owner-proven` |
| `Follow-up` | next action if the group remains open |
| `Status` | current implementation-tracking note |
## Invariants
- The same six failure categories must appear in `spec.md`, `plan.md`, `research.md`, `quickstart.md`, `tasks.md`, `checklists/requirements.md`, and `failure-classification.md`.
- The same five stabilization seams must appear in `spec.md`, `plan.md`, `research.md`, `quickstart.md`, `tasks.md`, `checklists/requirements.md`, and `failure-classification.md`.
- `294` introduces no new runtime product state and no new runtime persistence.
- `294` must not widen into route-cutover repair, full-suite repair, or a broader provider-boundary foundation package.
- Browser proof remains conditional and reuses the existing `Spec281ProviderConnectionScopeSmokeTest` only when visible provider-connection disclosure changes.
## Out-of-Scope Data Changes
- no database migrations by default
- no new provider-profile or verification-report persistence
- no new provider abstraction or registry
- no new verification schema version
- no new browser fixture family

View File

@ -0,0 +1,51 @@
# Failure Classification: Provider Verification Runtime Semantics Stabilization
## Baseline
Observed from:
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification
```
Current confirmed baseline at prep time: **11 failed, 98 passed, 755 assertions**.
Post-implementation verification for the bounded lane: **0 failed, 109 passed, 782 assertions**.
Grouping unit: one row below equals one failing test file or one failing scenario cluster that shares one seam and one category. The current 11 failing assertions collapse into 7 tracked groups.
## Pinned Categories
- `surface-or-report-baseline-drift`
- `fixture-contract-drift`
- `provider-verification-runtime-regression`
- `dedupe-concurrency-contract-drift`
- `out-of-scope-existing-debt`
- `resolved-or-not-needed`
## Pinned Seams
- `provider-neutrality-surface`
- `shared-startable-fixtures`
- `verification-start-contract`
- `provider-dispatch-concurrency`
- `verification-report-summary`
## Current Failing Groups
| Group | Seam | Category | Observed Failure | Candidate Owner | Fix In 294? | Follow-up | Status |
|---|---|---|---|---|---|---|---|
| `ProviderConnectionNeutralitySpec238Test` | `provider-neutrality-surface` | `surface-or-report-baseline-drift` | Current provider connection disclosure no longer matches one or more stale Spec 238 wording expectations, while the neutral target-scope summary remains the intended owner seam. | `ProviderConnectionSurfaceSummary`, target-scope descriptor/normalizer, touched resource detail/list expectations | yes | Resolved by refreshing the assertion to the current neutral `Provider context` baseline. | resolved |
| `ProviderDispatchGateStartSurfaceTest` | `provider-dispatch-concurrency` | `dedupe-concurrency-contract-drift` | Starting a different provider operation on the same active connection no longer returns the expected shared `scopeBusy` follow-through and queue/run count contract. | `ProviderOperationStartGate` | yes | Resolved after seeding the required provider-capability evidence the gate already expects. | resolved |
| `ProviderOperationConcurrencyTest` | `provider-dispatch-concurrency` | `dedupe-concurrency-contract-drift` | Multiple same-operation and cross-operation scenarios disagree with the current shared dedupe and `scopeBusy` contract, including run identity expectations. | `ProviderOperationStartGate`, one-hop `OperationRunService` identity path only if proven | yes | Resolved after aligning the startable provider-operation fixture prerequisites; no runtime owner change was needed. | resolved |
| `VerificationAuthorizationTest` | `shared-startable-fixtures` | `fixture-contract-drift` | An authorized operator receives `blocked` instead of `started`, which suggests the test's connection setup may no longer satisfy the canonical startable verification contract. | `tests/Pest.php`, touched verification fixture call sites, then `StartVerification` only if still wrong | yes | Resolved by using the canonical credential-enabled verification fixture path. | resolved |
| `VerificationStartAfterCompletionTest` | `verification-start-contract` | `provider-verification-runtime-regression` | A post-completion verification restart does not produce the expected fresh run and falls back to the wrong run identity or blocked behavior. | `StartVerification`, one-hop `OperationRunService` identity path only if proven | only-if-runtime-owner-proven | Resolved after fixture alignment; reruns proved the runtime owner contract was already correct. | resolved |
| `VerificationStartDedupeTest` | `verification-start-contract` | `provider-verification-runtime-regression` | Repeated verification starts return conflicting run keys instead of one canonical deduped run contract. | `StartVerification`, one-hop `OperationRunService` identity path only if proven | yes | Resolved after fixture alignment; reruns proved the runtime owner contract was already correct. | resolved |
| `ProviderConnectionHealthCheckWritesReportTest` | `verification-report-summary` | `surface-or-report-baseline-drift` | `summary.counts.total` now reflects a larger emitted check inventory than the stale hard-coded ceiling in the test. | `MicrosoftProviderHealthCheck`, directly implicated report writer seam, test expectation | yes | Resolved by removing the stale literal ceiling and pinning the count to the emitted check inventory. | resolved |
## Classification Rules
- Every in-scope failure group must keep exactly one seam and one category.
- If a rerun shows the failure is gone after adjacent fixture alignment, reclassify it to `resolved-or-not-needed` instead of widening runtime code.
- If a rerun proves the problem lives outside the bounded ProviderConnections/Verification scope, reclassify it to `out-of-scope-existing-debt` and stop widening `294`.
- Do not invent a new category or seam inside implementation without first updating `spec.md`, `plan.md`, `research.md`, `data-model.md`, `quickstart.md`, `tasks.md`, and `checklists/requirements.md`.

View File

@ -0,0 +1,253 @@
# Implementation Plan: Provider Verification Runtime Semantics Stabilization
**Branch**: `294-provider-verification-runtime-semantics` | **Date**: 2026-05-11 | **Spec**: [spec.md](./spec.md)
**Input**: Feature specification from `specs/294-provider-verification-runtime-semantics/spec.md`
**Note**: This plan is produced from the repo's Spec Kit workflow and remains preparation-only. It does not implement application code.
## Summary
Stabilize the remaining provider and verification runtime contract after Spec `293` by classifying the current failing lane first, then aligning the shared startable-fixture contract, the shared verification start gate, provider-operation dedupe/scope-busy behavior, provider-connection neutrality disclosure, and verification report summary counts to current repo truth. Keep route-cutover work, full-suite repair, new provider abstractions, and historical-spec rewrites out of scope.
## Inherited Baseline / Explicit Delta
### Inherited baseline
- Spec `293` already stabilized the route, workspace-aware link, and post-cutover panel baseline.
- The current remaining failing lane is confirmed by `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification`. The authoritative prep-time counts and grouped baseline live in `failure-classification.md`; this plan uses that baseline only as context.
- Historical Specs `238`, `188`, `084`, and `074` remain context only; none of them should be rewritten in this slice.
- The current repo already contains the owner seams this package should reuse: `StartVerification`, `ProviderOperationStartGate`, `OperationRunService`, `ProviderConnectionSurfaceSummary`, `ProviderConnectionTargetScopeNormalizer`, `ProviderConnectionTargetScopeDescriptor`, `MicrosoftProviderHealthCheck`, and `VerificationReportSchema`.
### Explicit delta in this plan
- Add one spec-local `failure-classification.md` artifact to pin the observed failure groups, their seams, and their candidate owners before implementation starts.
- Keep the implementation bounded to one feature lane and five pinned seam families.
- Repair fixture contract drift before widening runtime semantics.
- Repair surface/report baseline drift only through the existing shared summary/report owners.
- Reuse one existing provider-connection scope browser smoke only if visible provider-connection disclosure changes.
## Technical Context
**Language/Version**: PHP 8.4.15, Laravel 12.52
**Primary Dependencies**: Pest 4, Filament 5.2.1, Livewire 4.1.4, current provider-operation and verification support seams
**Storage**: no new runtime persistence; one spec-local `failure-classification.md` artifact only
**Testing**: focused Pest feature reruns in `tests/Feature/ProviderConnections` and `tests/Feature/Verification`, plus conditional reuse of one existing browser smoke
**Validation Lanes**: confidence, browser only when visible provider-connection disclosure changes
**Target Platform**: Laravel monolith in `apps/platform`
**Project Type**: web application
**Performance Goals**: keep the slice bounded to the existing feature lane and avoid turning it into a new broad repair lane
**Constraints**: no full-suite repair, no route-cutover repair, no new provider abstraction, no new schema version, no new panel, no new notification family, and no historical-spec rewrites
**Scale/Scope**: one provider/verification stabilization slice covering 11 currently failing assertions across seven feature files and a small set of shared owner seams
## Pinned Failure-Classification Categories
- `surface-or-report-baseline-drift`
- `fixture-contract-drift`
- `provider-verification-runtime-regression`
- `dedupe-concurrency-contract-drift`
- `out-of-scope-existing-debt`
- `resolved-or-not-needed`
## Pinned Stabilization Seams
- `provider-neutrality-surface`
- `shared-startable-fixtures`
- `verification-start-contract`
- `provider-dispatch-concurrency`
- `verification-report-summary`
## Likely Affected Repo Surfaces
- `apps/platform/tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php`
- `apps/platform/tests/Feature/ProviderConnections/ProviderDispatchGateStartSurfaceTest.php`
- `apps/platform/tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php`
- `apps/platform/tests/Feature/Verification/VerificationAuthorizationTest.php`
- `apps/platform/tests/Feature/Verification/VerificationStartAfterCompletionTest.php`
- `apps/platform/tests/Feature/Verification/VerificationStartDedupeTest.php`
- `apps/platform/tests/Feature/Verification/ProviderConnectionHealthCheckWritesReportTest.php`
- `apps/platform/tests/Pest.php`
- `apps/platform/app/Services/Verification/StartVerification.php`
- `apps/platform/app/Services/Providers/ProviderOperationStartGate.php`
- `apps/platform/app/Services/OperationRunService.php` only if targeted reruns prove the run-identity owner needs a one-hop correction
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionSurfaceSummary.php`
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionTargetScopeNormalizer.php`
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionTargetScopeDescriptor.php`
- `apps/platform/app/Services/Providers/MicrosoftProviderHealthCheck.php`
- `apps/platform/app/Support/Verification/VerificationReportSchema.php`
- existing verification report writer or provider-connection resource/view seams only if targeted reruns prove they are the direct owners
## Filament v5 / Surface Notes
- **Livewire v4.0+ compliance**: all touched admin surfaces remain on Filament v5 with Livewire v4
- **Provider registration location**: unchanged in `apps/platform/bootstrap/providers.php`
- **Global search rule**: `ProviderConnectionResource` remains non-globally-searchable; no new searchable resource is introduced
- **Destructive actions**: no new destructive action is planned; touched existing mutations must preserve `->action(...)`, `->requiresConfirmation()`, and server-side authorization
- **Asset strategy**: no asset registration or `filament:assets` deployment-step change is planned
## Stabilization Fit
- classify the target lane before changing runtime or tests
- align fixture contract drift before classifying runtime regressions
- prefer fixing stale expectations through the current shared owners instead of page-local or test-local shims
- allow a runtime fix only when the targeted rerun proves a canonical shared owner is wrong after fixture alignment
- keep visible provider-scope and report disclosure changes behind existing surfaces only
- keep remaining unrelated debt explicit in `failure-classification.md`
## UI / Surface Guardrail Plan
- **Guardrail scope**: existing provider connection resource surfaces, existing verification entry points, and existing verification report detail surfaces only
- **Native vs custom classification summary**: native Filament resource/detail surfaces and existing shared detail surfaces only
- **Shared-family relevance**: provider connection summary family, verification start-result family, and verification report family
- **State layers in scope**: table, detail, form, action-result follow-through, and DB-backed report detail only
- **Audience modes in scope**: operator-MSP, support-platform, and readonly-entitled report viewers
- **Decision/diagnostic/raw hierarchy plan**: neutral target-scope summary and report summary stay decision-first; nested provider identity detail and per-check evidence remain secondary
- **Raw/support gating plan**: raw payloads remain absent; provider-owned identity detail remains nested only
- **One-primary-action / duplicate-truth control**: no new action family; existing primary actions stay primary and no duplicate summary layer is introduced
- **Handling modes by drift class or surface**: in-scope provider/verification drift is implementation-required; unrelated debt is report-only in `failure-classification.md`
- **Repository-signal treatment**: review-mandatory if implementation tries to widen into route cutover, full-suite repair, or new provider abstractions
- **Special surface test profiles**: `standard-native-filament`, `shared-detail-family`
- **Required tests or manual smoke**: focused feature lane always; one existing provider-connection scope browser smoke only when visible provider-connection disclosure changes
- **Exception path and spread control**: no exception path is planned; if implementation would need one, stop and reclassify rather than widen silently
- **Active feature PR close-out entry**: `RuntimeSemantics`
## Shared Pattern & System Fit
- **Cross-cutting feature marker**: yes
- **Systems touched**: current provider connection summary, current verification start path, current provider-operation start gate, current verification report schema/writer path, and the current shared test-fixture contract
- **Shared abstractions reused**: `StartVerification`, `ProviderOperationStartGate`, `OperationRunService`, `ProviderConnectionSurfaceSummary`, `ProviderConnectionTargetScopeNormalizer`, `ProviderConnectionTargetScopeDescriptor`, and `VerificationReportSchema`
- **New abstraction introduced? why?**: none
- **Why the existing abstraction was sufficient or insufficient**: the abstractions already exist and own the relevant behavior; the missing piece is contract stabilization, not a new framework
- **Bounded deviation / spread control**: runtime changes are allowed only inside the current owner seams and only after fixture alignment proves the current owner is at fault
## OperationRun UX Impact
- **Touches OperationRun start/completion/link UX?**: yes
- **Central contract reused**: `ProviderOperationStartGate`, `StartVerification`, `OperationRunService`, and existing run-link helpers
- **Delegated UX behaviors**: `started`, `deduped`, `scopeBusy`, `blocked`, queue-dispatch decisions, and canonical run follow-through links remain delegated to the shared owners above
- **Surface-owned behavior kept local**: existing provider connection and verification entry points keep initiation placement only
- **Queued DB-notification policy**: unchanged
- **Terminal notification path**: unchanged
- **Exception path**: none
## Provider Boundary & Portability Fit
- **Shared provider/platform boundary touched?**: yes
- **Provider-owned seams**: nested Microsoft tenant identity details and provider-specific troubleshooting context
- **Platform-core seams**: neutral target-scope summary, verification start/result semantics, dedupe/scope-busy behavior, and report summary truth
- **Neutral platform terms / contracts preserved**: `provider connection`, `target scope`, `provider context`, `verification report`, `started`, `deduped`, `scope busy`, `blocked`
- **Retained provider-specific semantics and why**: Microsoft identity detail remains nested because current consent and verification troubleshooting still need it
- **Bounded extraction or follow-up path**: broader provider-boundary cleanup stays deferred; `294` only stabilizes the confirmed failing lane
## Constitution Check
*GATE: Must pass before implementation begins and again after the design artifacts are complete.*
- `Inventory-first, Snapshots-second`: PASS. `294` introduces no new inventory or snapshot truth.
- `Read/Write Separation by Default`: PASS. The slice stabilizes existing start/report behavior only.
- `Single Contract Path to Graph`: PASS by preservation. No new Graph seam is introduced.
- `PROV-001`: PASS. Shared neutral disclosure stays primary, provider-specific identity detail stays nested.
- `PROP-001`, `ABSTR-001`, `V1-EXP-001`, and `LAYER-001`: PASS. No new framework or layer is introduced.
- `PERSIST-001` and `STATE-001`: PASS. The only new vocabulary is spec-local failure classification, not runtime persistence or product state.
- `XCUT-001`: PASS. Existing shared provider/verification owners are reused.
- `RBAC-UX`: PASS by preservation. Non-member `404`, in-scope no-capability `403`, and no new raw capability checks are introduced.
- `TEST-GOV-001`: PASS. Proof stays in the narrowest honest feature lane with one conditional existing browser smoke only when needed.
- `UI-FIL-001`: PASS. Existing Filament surfaces remain native; no custom surface or asset family is introduced.
- `LEAN-001`: PASS. No compatibility shim, no legacy alias, and no historical-spec rewrite is planned.
**Gate evaluation**: PASS.
**Post-design re-check**: PASS while the same failure categories, seam names, proving commands, and out-of-scope boundary remain aligned across `spec.md`, `plan.md`, `research.md`, `data-model.md`, `quickstart.md`, `failure-classification.md`, `tasks.md`, and `checklists/requirements.md`.
## Test Governance Check
- **Test purpose / classification by changed surface**: Feature, with conditional Browser reuse only when visible provider-connection disclosure changes
- **Affected validation lanes**: confidence and conditional browser
- **Why this lane mix is the narrowest sufficient proof**: the failing contract already lives honestly in the targeted feature lane. Optional browser proof is justified only when visible provider-connection disclosure changes on a real provider surface. Verification-report disclosure remains bounded to feature-lane proof in `294`.
- **Narrowest proving command(s)**:
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Verification/VerificationAuthorizationTest.php tests/Feature/Verification/VerificationStartAfterCompletionTest.php tests/Feature/Verification/VerificationStartDedupeTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderDispatchGateStartSurfaceTest.php tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php tests/Feature/Verification/ProviderConnectionHealthCheckWritesReportTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
- **Fixture / helper / factory / seed / context cost risks**: moderate only around the startable-fixture contract; no broader provider context should become implicit in more tests
- **Expensive defaults or shared helper growth introduced?**: no; fixture alignment should reduce hidden drift, not widen helper cost
- **Heavy-family additions, promotions, or visibility changes**: none
- **Surface-class relief / special coverage rule**: `standard-native-filament` and `shared-detail-family` remain sufficient
- **Closing validation and reviewer handoff**: reviewers must rerun the exact commands above, confirm the targeted lane is green, and only require the existing provider-connection scope browser smoke if visible provider-connection disclosure changed
- **Budget / baseline / trend follow-up**: none beyond feature-local upkeep
- **Review-stop questions**: did the work widen into route cutover, full-suite repair, or a new provider framework; did it change visible provider/report disclosure without rerunning the named existing browser smoke
- **Escalation path**: `document-in-feature` for residual notes; `reject-or-split` for scope expansion
- **Active feature PR close-out entry**: `RuntimeSemantics`
- **Why no dedicated follow-up spec is needed**: `294` itself is the dedicated follow-up for the remaining provider/verification lane after `293`
## Review Checklist Status
- **Review checklist artifact**: `checklists/requirements.md`
- **Review outcome class**: `acceptable-special-case`
- **Workflow outcome**: `keep`
- **Test-governance outcome**: `keep`
- **Resolution note**: the package is implementation-ready as one bounded provider/verification stabilization slice following Spec `293`
- **Escalation rule**: if implementation starts repairing unrelated full-suite debt, route cutover, or broader provider-boundary work, stop and split it out of `294`
## Rollout Considerations
- record the currently observed failing groups in `failure-classification.md` before changing tests or runtime
- align fixture contract drift before calling anything a runtime regression
- stabilize verification start semantics before provider-operation concurrency semantics, because both depend on the same gate ownership
- stabilize surface/report baseline drift after the runtime start contract settles, so tests do not chase a moving owner contract
- rerun the full targeted lane after each bounded slice, not just isolated files
- use the existing provider-connection scope browser smoke only when visible provider-connection disclosure actually changes
## Risk Controls
- reject any implementation that widens into full-suite repair outside the targeted lane
- reject any implementation that reopens route-cutover or workspace-aware link work already handled by Spec `293`
- reject any implementation that adds a new provider framework, new profile table, or new verification schema version
- reject any implementation that fixes visible disclosure drift only in tests while leaving the shared summary/report owner inconsistent
- reject any implementation that leaves remaining target-lane failures unclassified in `failure-classification.md`
## Research & Design Outputs
- `research.md` records the stabilization decisions, rejected alternatives, and evidence anchors
- `data-model.md` captures the pinned failure categories, seam inventory, and `failure-classification.md` structure
- `quickstart.md` gives reviewers the read order, review scenarios, exact proof commands, and stop conditions
- `failure-classification.md` records the confirmed current failing groups, their seams, categories, candidate owners, and bounded follow-up status
- `checklists/requirements.md` records the readiness and bounded-scope checks for the package
## Project Structure
### Documentation (this feature)
```text
specs/294-provider-verification-runtime-semantics/
├── checklists/
│ └── requirements.md
├── data-model.md
├── failure-classification.md
├── plan.md
├── quickstart.md
├── research.md
├── spec.md
└── tasks.md
```
### Source Code (repository root)
```text
apps/platform/
├── app/
│ ├── Services/
│ └── Support/
└── tests/
├── Browser/
└── Feature/
```
**Structure Decision**: keep the package inside the existing Laravel app and test structure. Reuse current provider/verification tests, shared owner seams, and one existing browser smoke instead of inventing a new stabilization subsystem.
## Complexity Tracking
| Violation | Why Needed | Simpler Alternative Rejected Because |
|---|---|---|
| Spec-local failure-classification vocabulary | The lane needs one shared bounded way to separate fixture drift, runtime regression, concurrency drift, and surface/report drift before implementation | Ad hoc notes or one-off fixes would make the package drift back into unsourced local patches |

View File

@ -0,0 +1,140 @@
# Quickstart: Provider Verification Runtime Semantics Stabilization
## Purpose
`294` is the bounded follow-up to Spec `293`. It exists only to stabilize the remaining provider and verification runtime semantics block still failing inside:
- `tests/Feature/ProviderConnections`
- `tests/Feature/Verification`
It is not a full-suite repair, not a route-cutover package, and not a broader provider-boundary refresh.
## Preconditions
- Stay on branch `294-provider-verification-runtime-semantics`.
- Run all PHP, Artisan, and formatter commands through Sail.
- Keep the macOS PATH workaround when using terminal commands in this workspace:
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH"
```
- Treat Spec `293` as inherited context only.
- Do not implement any route-cutover or `/admin/t/...` repair under this package.
## Read Order
1. `spec.md`
2. `plan.md`
3. `research.md`
4. `data-model.md`
5. `failure-classification.md`
6. `tasks.md`
7. `checklists/requirements.md`
## Implementation Intent
- Classify the current failing lane before changing tests or runtime.
- Clear fixture-contract drift before widening runtime changes.
- Reuse the current shared owners: `StartVerification`, `ProviderOperationStartGate`, `OperationRunService`, `ProviderConnectionSurfaceSummary`, and `VerificationReportSchema`.
- Keep provider-neutral disclosure primary and provider-specific identity detail nested.
- Keep validation bounded to the target feature lane, with one conditional existing provider-connection scope browser smoke only if provider-connection disclosure changes visibly.
## Pinned Failure-Classification Categories
- `surface-or-report-baseline-drift`
- `fixture-contract-drift`
- `provider-verification-runtime-regression`
- `dedupe-concurrency-contract-drift`
- `out-of-scope-existing-debt`
- `resolved-or-not-needed`
## Pinned Stabilization Seams
- `provider-neutrality-surface`
- `shared-startable-fixtures`
- `verification-start-contract`
- `provider-dispatch-concurrency`
- `verification-report-summary`
## Grouping Unit
`294` tracks one failure group per failing test file or failing scenario cluster that shares one seam and one category. The authoritative grouped baseline lives in `failure-classification.md`.
## Review Scenarios
### Scenario 1: Verification start contract
- Canonical startable verification fixtures should return `started`.
- Repeated active starts should return `deduped`.
- Post-completion starts should create a new run.
- `blocked` should remain reserved for real prerequisite failures.
### Scenario 2: Provider-operation concurrency
- Same-operation active reruns should dedupe.
- Different active operations on the same provider connection should return one shared `scopeBusy` follow-through.
- Queue counts and created runs must stay aligned with the shared gate contract.
### Scenario 3: Surface and report baselines
- Provider connection surfaces should keep neutral target-scope disclosure as the primary truth.
- Provider-specific Microsoft identity detail may stay nested, but must not replace the neutral disclosure.
- Verification report counts must track the current emitted check inventory, not a stale literal ceiling.
## Planned Proof Commands
### Full in-scope lane
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification
```
### Verification start cluster
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Verification/VerificationAuthorizationTest.php tests/Feature/Verification/VerificationStartAfterCompletionTest.php tests/Feature/Verification/VerificationStartDedupeTest.php
```
### Provider dispatch and concurrency cluster
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderDispatchGateStartSurfaceTest.php tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php
```
### Surface and report baseline cluster
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php tests/Feature/Verification/ProviderConnectionHealthCheckWritesReportTest.php
```
### Conditional existing browser smoke
Run this only if implementation changes visible provider-connection disclosure:
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php
```
### Formatting
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent
```
## Stop Conditions
Stop and split the work out of `294` if any of the following becomes necessary:
- route-cutover or `/admin/t/...` repair
- full-suite cleanup outside `tests/Feature/ProviderConnections` and `tests/Feature/Verification`
- a new provider abstraction, provider-profile table, capability registry, or verification schema version
- reopening historical Specs `238`, `188`, `084`, or `074` as implementation targets
- adding a new browser family instead of reusing the existing provider-scope smoke
## Expected Close-Out State
- the full in-scope ProviderConnections/Verification lane is green
- `failure-classification.md` reflects the final disposition of every currently failing group
- any visible provider-connection disclosure change is backed by the existing provider-connection scope browser smoke
- no out-of-scope debt has been silently absorbed

View File

@ -0,0 +1,103 @@
# Research: Provider Verification Runtime Semantics Stabilization
## Pinned Failure-Classification Categories
`294` uses exactly these spec-local categories:
- `surface-or-report-baseline-drift`
- `fixture-contract-drift`
- `provider-verification-runtime-regression`
- `dedupe-concurrency-contract-drift`
- `out-of-scope-existing-debt`
- `resolved-or-not-needed`
These categories are implementation-tracking vocabulary only. They do not become runtime product state.
## Pinned Stabilization Seams
`294` stabilizes exactly these seam families:
- `provider-neutrality-surface`
- `shared-startable-fixtures`
- `verification-start-contract`
- `provider-dispatch-concurrency`
- `verification-report-summary`
## Decision 1: Classify the targeted failing lane before changing anything
- The package starts with the already confirmed failing command `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification`.
- `failure-classification.md` records every currently failing group before any implementation work begins.
- This keeps `294` bounded to the confirmed provider/verification lane instead of silently widening into broader suite repair.
## Decision 2: Fixture contract drift must be cleared before runtime semantics are widened
- Current repo truth already has a canonical startable verification fixture path: `createUserWithTenant(..., fixtureProfile: 'credential-enabled')` or an equivalent helper path that seeds a truthful startable Microsoft provider connection.
- Hand-built provider connections that look plausible but miss canonical prerequisites may legitimately block before queue dispatch.
- Therefore, implementation must align startable fixtures first and only treat remaining mismatches as runtime regressions after that alignment.
## Decision 3: `StartVerification` and `ProviderOperationStartGate` remain the runtime owners
- `StartVerification` owns provider verification start, dedupe, and new-run-after-completion behavior.
- `ProviderOperationStartGate` owns shared provider-operation dedupe versus `scopeBusy` behavior, stale-queued cleanup, and blocked-start behavior.
- `294` should stabilize those owners, not add a new semantics layer around them.
## Decision 4: Provider-neutral disclosure stays primary, provider-specific identity detail stays nested
- `ProviderConnectionSurfaceSummary` and the target-scope descriptor/normalizer already define the neutral summary path.
- The current failing neutrality test proves the detail baseline drift lives at that disclosure seam, not in a broader route or panel contract.
- Microsoft tenant identity detail remains allowed as nested provider-owned context, but it should not replace the neutral top-line summary.
## Decision 5: Verification report totals must track the current emitted inventory
- `ProviderConnectionHealthCheckWritesReportTest` currently fails because the report emits more checks than a stale ceiling expects.
- `VerificationReportSchema` validates shape, not the feature-local ceiling.
- Therefore, the implementation should anchor test expectations to the current emitted inventory and current report writer behavior, not to a stale literal maximum.
## Decision 6: Browser proof stays conditional and reused, not expanded
- The primary proof lane is the targeted ProviderConnections/Verification feature suite.
- If implementation changes visible provider-connection disclosure, reuse the existing `tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php` only. Verification-report disclosure remains bounded to feature-lane proof in `294`.
- Do not add a new browser family or a second broad smoke lane under `294`.
## Rejected Alternatives
### Rejected: fold the work back into Spec `293`
That would blur the route-cutover stabilization boundary and make the post-cutover handoff ambiguous.
### Rejected: reopen Spec `238`, Spec `188`, Spec `084`, or Spec `074`
Those packages are historical context. `294` should treat them as context and stabilize only the current remaining failing lane.
### Rejected: split provider and verification into separate micro-specs
The same start gate, fixture contract, and report summary seams would be duplicated across two packages.
### Rejected: widen to a full-suite or broader provider-boundary repair
That would exceed the user-provided bounded scope and bury the actual remaining seam under unrelated work.
## Evidence Anchors
- `apps/platform/tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php`
- `apps/platform/tests/Feature/ProviderConnections/ProviderDispatchGateStartSurfaceTest.php`
- `apps/platform/tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php`
- `apps/platform/tests/Feature/Verification/VerificationAuthorizationTest.php`
- `apps/platform/tests/Feature/Verification/VerificationStartAfterCompletionTest.php`
- `apps/platform/tests/Feature/Verification/VerificationStartDedupeTest.php`
- `apps/platform/tests/Feature/Verification/ProviderConnectionHealthCheckWritesReportTest.php`
- `apps/platform/tests/Pest.php`
- `apps/platform/app/Services/Verification/StartVerification.php`
- `apps/platform/app/Services/Providers/ProviderOperationStartGate.php`
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionSurfaceSummary.php`
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionTargetScopeNormalizer.php`
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionTargetScopeDescriptor.php`
- `apps/platform/app/Services/Providers/MicrosoftProviderHealthCheck.php`
- `apps/platform/app/Support/Verification/VerificationReportSchema.php`
## Boundary Summary
- `294` is a provider/verification stabilization package, not a route-cutover package.
- `293` remains untouched.
- Historical Specs `238`, `188`, `084`, and `074` remain untouched.
- The package keeps the proving lane bounded to the targeted feature folders and one conditional existing browser smoke.

View File

@ -0,0 +1,369 @@
# Feature Specification: Provider Verification Runtime Semantics Stabilization
**Feature Branch**: `294-provider-verification-runtime-semantics`
**Created**: 2026-05-11
**Status**: Ready
**Input**: User description: "Prepare Spec 294 as the bounded follow-up to Spec 293. Keep scope limited to the remaining provider/verification failure block in `tests/Feature/ProviderConnections` and `tests/Feature/Verification`, covering provider/verification runtime semantics, fixture contracts, dedupe/concurrency behavior, and small surface/report baseline drifts. No application implementation in this prep step."
## Spec Candidate Check *(mandatory - SPEC-GATE-001)*
- **Problem**: Spec `293` stabilized the post-cutover route and workspace-aware baseline, but the remaining red block is now concentrated in provider and verification runtime semantics. The current repo truth still leaves disagreement between provider connection surface expectations, startable verification fixture contracts, shared start-gate dedupe/scope-busy behavior, and verification report count baselines.
- **Today's failure**: at prep time, the confirmed targeted lane `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification` grouped 11 failing assertions into 7 tracked failure groups. The authoritative baseline snapshot lives in `failure-classification.md`. Those groups cover one provider-neutrality detail drift, one provider-dispatch start-surface mismatch, one grouped provider-operation concurrency cluster, one verification report summary-count drift, one verification authorization start mismatch, one grouped verification restart/dedupe cluster, and one grouped verification post-completion cluster. None of those failures are primarily `/admin/t/...` or workspace-route cutover issues anymore.
- **User-visible improvement**: operators and maintainers get one truthful provider/verification contract again: canonical startable verification flows start or dedupe when the connection is truly startable, blocked states stay reserved for real prerequisite failures, cross-operation scope-busy behavior remains consistent, provider-connection detail surfaces keep neutral target-scope disclosure with provider-specific identity detail nested intentionally, and verification report summaries reflect the current emitted check inventory instead of stale hard-coded totals.
- **Smallest enterprise-capable version**: one stabilization spec that classifies the remaining provider/verification failure groups first, aligns the shared startable-fixture contract, keeps `StartVerification` and `ProviderOperationStartGate` as the single runtime owners of started/deduped/scope-busy/blocked semantics, refreshes the provider-connection neutrality baseline and verification report summary-count baseline to current repo truth, and validates the result with the targeted ProviderConnections/Verification lane plus conditional reuse of the existing provider-connection scope browser smoke only when provider-connection disclosure changes visibly.
- **Explicit non-goals**: no full-suite repair, no route-cutover repair, no Package Execution work, no Guided Operations work, no new provider abstraction or provider-profile table, no new verification schema version, no new panel, no new globally-searchable resource, no new notification policy, no broad UX redesign, and no reopening of historical Specs `238`, `188`, `084`, or `074` as refresh targets.
- **Permanent complexity imported**: one spec-local `failure-classification.md` artifact, one bounded failure-category inventory, one bounded seam inventory, and focused tasks against existing provider/verification owner seams. No new runtime persistence, no new abstraction family, and no new product surface are introduced.
- **Why now**: after Spec `293`, this is the only confirmed remaining red cluster that is still tightly bounded and repo-ready. Leaving it unresolved would force later provider, verification, or quality-gate work to absorb the same runtime and fixture drift repeatedly.
- **Why not local**: the failures span shared runtime owners (`StartVerification`, `ProviderOperationStartGate`, `OperationRunService` identity behavior if implicated), shared provider summary output, verification report production, and the test-fixture contract in `tests/Pest.php` and target lane call sites. A one-file patch would hide rather than solve the contract disagreement.
- **Approval class**: Cleanup
- **Red flags triggered**: cross-cutting runtime stabilization, temptation to fold the work back into Spec `293`, and temptation to widen into a broader provider-boundary or full-suite repair. Defense: the package is explicitly pinned to one confirmed failing lane, one failure-classification vocabulary, and existing owner seams only.
- **Score**: Nutzen: 2 | Dringlichkeit: 2 | Scope: 2 | Komplexitaet: 1 | Produktnaehe: 1 | Wiederverwendung: 2 | **Gesamt: 10/12**
- **Decision**: approve
## Review Outcome
- **Outcome class**: `acceptable-special-case`
- **Workflow outcome**: `keep`
- **Test-governance outcome**: `keep`
- **Reason**: the active auto queue remains intentionally empty, but this user-provided manual follow-up is still justified because current repo truth shows one bounded remaining provider/verification runtime seam after Spec `293`, and the package stays narrower than reopening a broader cutover or provider-boundary lane.
- **Workflow result**: Ready for implementation as one bounded provider/verification stabilization slice that follows Spec `293` without rewriting it.
## Pinned Failure-Classification Categories
- `surface-or-report-baseline-drift`
- `fixture-contract-drift`
- `provider-verification-runtime-regression`
- `dedupe-concurrency-contract-drift`
- `out-of-scope-existing-debt`
- `resolved-or-not-needed`
## Pinned Stabilization Seams
- `provider-neutrality-surface`
- `shared-startable-fixtures`
- `verification-start-contract`
- `provider-dispatch-concurrency`
- `verification-report-summary`
## Candidate Selection Gate
- **Selected candidate**: Provider Verification Runtime Semantics Stabilization
- **Source location**: explicit user-provided follow-up candidate anchored to `specs/293-post-cutover-suite-stabilization/` plus the confirmed failing command `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification`
- **Why selected now**: the repo no longer shows route-cutover or workspace-link debt as the primary blocker inside this lane; the remaining failures now sit exactly on provider/verification runtime seams that later features would otherwise keep rediscovering.
- **Why close alternatives were deferred**:
- refreshing Spec `293` directly would blur the handoff boundary between route-cutover stabilization and provider/verification runtime follow-up
- reopening Spec `238` would rewrite a historical package instead of treating its failing surface assertion as stabilization context
- reopening Spec `188`, Spec `084`, or Spec `074` would mix broader historical cleanup or feature foundations back into one targeted follow-up
- splitting provider and verification into separate micro-specs would duplicate the same start gate, fixture, and report seams across two packages
- widening to a broader full-suite repair would exceed the user-provided bounded goal
- **Roadmap relationship**: post-cutover stabilization follow-through within the current workspace-first and provider-boundary hardening trajectory; this is not a new roadmap theme or an automatic next-best-prep queue promotion
- **Completed-spec guardrail result**: Spec `238` already carries implementation close-out history and is context only; Spec `293` already exists as its own stabilization package with checklist and failure-classification artifacts and must not be rewritten; Specs `188`, `084`, and `074` remain historical foundation context and are excluded from refresh in this prep pass
- **Smallest viable implementation slice**: classify and fix only the current failing ProviderConnections/Verification lane across shared startable fixtures, shared start-gate semantics, provider-neutrality surface baseline, and verification report summary counts
- **Proposed concise feature description to feed into specify**: Stabilize the remaining provider connection and verification runtime contract so current start, dedupe, scope-busy, neutral disclosure, and report-summary behavior match one canonical repo-truth baseline after Spec 293
## Spec Scope Fields *(mandatory)*
- **Scope**: workspace + managed-environment + canonical operations follow-through
- **Primary Routes**:
- `/admin/provider-connections`
- `/admin/provider-connections/{record}`
- `/admin/provider-connections/{record}/edit`
- existing workspace/environment entry points that launch provider verification or provider-backed operations through the admin panel
- canonical `admin.operations.view` follow-through for verification report detail
- **Data Ownership**:
- `ProviderConnection` remains the existing workspace-owned, managed-environment-scoped provider binding record
- `ProviderCredential` remains the existing provider-owned secret record attached to one `ProviderConnection`
- `OperationRun` remains execution truth, and `context.verification_report` remains the only persisted verification report artifact used by this slice
- no new persisted table, profile record, or registry is introduced
- `failure-classification.md` is a spec-local prep artifact only and is not runtime truth
- **RBAC**:
- workspace membership remains the first entitlement boundary
- managed-environment access remains the second entitlement boundary
- existing provider-view and provider-run capabilities continue to gate provider connection detail, verification start, and provider-backed operations
- non-members or wrong-scope actors remain `404`
- in-scope actors missing capability remain `403`
For canonical-view specs, the spec MUST define:
- **Default filter behavior when tenant-context is active**: N/A - this slice does not add a new canonical-view surface
- **Explicit entitlement checks preventing cross-tenant leakage**: existing provider-connection and verification follow-through surfaces continue to require workspace plus managed-environment entitlement, and this slice must not loosen that boundary while stabilizing start/report semantics
## Cross-Cutting / Shared Pattern Reuse *(mandatory when the feature touches notifications, status messaging, action links, header actions, dashboard signals/cards, alerts, navigation entry points, evidence/report viewers, or any other existing shared operator interaction family; otherwise write `N/A - no shared interaction family touched`)*
- **Cross-cutting feature?**: yes
- **Interaction class(es)**: provider connection detail summaries, verification start results, canonical run follow-through, verification report summaries, and shared test-fixture contracts
- **Systems touched**:
- `apps/platform/tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php`
- `apps/platform/tests/Feature/ProviderConnections/ProviderDispatchGateStartSurfaceTest.php`
- `apps/platform/tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php`
- `apps/platform/tests/Feature/Verification/VerificationAuthorizationTest.php`
- `apps/platform/tests/Feature/Verification/VerificationStartAfterCompletionTest.php`
- `apps/platform/tests/Feature/Verification/VerificationStartDedupeTest.php`
- `apps/platform/tests/Feature/Verification/ProviderConnectionHealthCheckWritesReportTest.php`
- `apps/platform/tests/Pest.php`
- `apps/platform/app/Services/Verification/StartVerification.php`
- `apps/platform/app/Services/Providers/ProviderOperationStartGate.php`
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionSurfaceSummary.php`
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionTargetScopeNormalizer.php`
- `apps/platform/app/Support/Providers/TargetScope/ProviderConnectionTargetScopeDescriptor.php`
- `apps/platform/app/Services/Providers/MicrosoftProviderHealthCheck.php`
- `apps/platform/app/Support/Verification/VerificationReportSchema.php`
- existing `OperationRunService`, `VerificationReportWriter`, and provider-connection resource or view seams only if the targeted reruns prove they are the direct owners
- **Existing pattern(s) to extend**: the current provider-operation start gate, the current verification start path, the current provider target-scope summary path, the current verification report schema/writer path, and the repo's current `createUserWithTenant(..., fixtureProfile: 'credential-enabled')` startable-fixture contract
- **Shared contract / presenter / builder / renderer to reuse**: `StartVerification`, `ProviderOperationStartGate`, `OperationRunService`, `ProviderConnectionSurfaceSummary`, `ProviderConnectionTargetScopeNormalizer`, `ProviderConnectionTargetScopeDescriptor`, and `VerificationReportSchema`
- **Why the existing shared path is sufficient or insufficient**: the repo already has the correct shared owners; the missing piece is stabilizing their current contract against stale fixture assumptions, stale report-count baselines, and stale surface assertions rather than introducing a new provider or verification framework
- **Allowed deviation and why**: only minimal runtime fixes are allowed later if a targeted rerun proves a canonical shared owner is wrong after fixture alignment. Reopening route cutover, adding a new provider abstraction, or broadening verification schema scope is forbidden.
- **Consistency impact**: provider connection detail, verification start outcomes, provider-operation concurrency, and verification report summaries must all describe the same connection and the same run-state contract
- **Review focus**: reviewers must verify that implementation reuses the existing shared owners, aligns fixtures before widening runtime changes, and keeps the package inside the confirmed failing lane
## OperationRun UX Impact *(mandatory when the feature creates, queues, deduplicates, resumes, blocks, completes, or deep-links to an `OperationRun`; otherwise write `N/A - no OperationRun start or link semantics touched`)*
- **Touches OperationRun start/completion/link UX?**: yes
- **Shared OperationRun UX contract/layer reused**: `ProviderOperationStartGate`, `StartVerification`, `OperationRunService`, `OperationRunLinks`, and the existing provider-operation start result path
- **Delegated start/completion UX behaviors**: current shared start-gate logic remains responsible for `started`, `deduped`, `scopeBusy`, and `blocked` semantics, queue-dispatch decisions, and canonical run follow-through links; this slice only stabilizes the conditions under which those outcomes occur and how they are asserted
- **Local surface-owned behavior that remains**: existing provider-connection list/detail actions and existing verification entry points continue to own only initiation inputs and local follow-up placement
- **Queued DB-notification policy**: `N/A` - unchanged
- **Terminal notification path**: unchanged central lifecycle mechanism
- **Exception required?**: none
## Provider Boundary / Platform Core Check *(mandatory when the feature changes shared provider/platform seams, identity scope, governed-subject taxonomy, compare strategy selection, provider connection descriptors, or operator vocabulary that may leak provider-specific semantics into platform-core truth; otherwise write `N/A - no shared provider/platform boundary touched`)*
- **Shared provider/platform boundary touched?**: yes
- **Boundary classification**: mixed
- **Seams affected**: provider connection default-visible summary language, nested provider identity detail, provider-operation start context, verification report summary counts, and verification start result semantics
- **Neutral platform terms preserved or introduced**: `provider connection`, `target scope`, `provider context`, `verification report`, `credential source`, `effective client identity`, `started`, `deduped`, `scope busy`, and `blocked`
- **Provider-specific semantics retained and why**: Microsoft tenant identifiers and contextual identity details remain provider-owned nested detail where current consent, verification, and troubleshooting flows still need them
- **Why this does not deepen provider coupling accidentally**: this slice stabilizes the shared neutral summary and start contract while keeping Microsoft-specific detail nested instead of re-promoting it into the primary shared surface or shared run outcome truth
- **Follow-up path**: broader provider-boundary or future multi-provider work stays deferred; `294` only stabilizes the currently failing provider/verification lane
## UI / Surface Guardrail Impact *(mandatory when operator-facing surfaces are changed; otherwise write `N/A`)*
| Surface / Change | Operator-facing surface change? | Native vs Custom | Shared-Family Relevance | State Layers Touched | Exception Needed? | Low-Impact / `N/A` Note |
|---|---|---|---|---|---|---|
| Provider connection resource list/detail/edit baselines | yes | Native Filament resource plus shared provider summary primitives | provider connection summary and action family | table, detail, form | no | Existing surfaces only; no new route family or resource |
| Verification start results on existing provider and verification entry points | yes | Existing action surfaces over shared start-gate logic | start result semantics, run-link follow-through, blocked guidance | action, notification, route follow-through | no | No new action family or notification family |
| Verification report viewer / operations detail baseline | yes | Existing shared detail surfaces and DB-backed report viewer | verification report summary and disclosure order | detail, page | no | Existing viewer only; no new report surface |
## Decision-First Surface Role *(mandatory when operator-facing surfaces are changed)*
| Surface | Decision Role | Human-in-the-loop Moment | Immediately Visible for First Decision | On-Demand Detail / Evidence | Why This Is Primary or Why Not | Workflow Alignment | Attention-load Reduction |
|---|---|---|---|---|---|---|---|
| Provider connection resource family | Primary Decision Surface | Operator decides whether the connection is usable and whether verification or another provider action should start | target scope, consent, verification, and current readiness summary | nested provider identity details, capability detail, and recent run follow-through | Primary because this is the surface that owns the provider binding and its immediate next actions | follows the existing provider-connection workflow | removes the need to infer scope from stale or duplicated labels |
| Verification report viewer / operations detail | Secondary Context Surface | Operator confirms what the latest verification run actually proved after a start, dedupe, or blocked outcome | overall summary, counts, and current next steps | per-check evidence pointers and nested provider-owned detail | Secondary because it explains the result of a previously made action rather than owning the action itself | stays aligned with current operations follow-through | keeps run evidence behind the existing report viewer instead of duplicating it on start surfaces |
## Audience-Aware Disclosure *(mandatory when operator-facing surfaces are changed)*
| Surface | Audience Modes In Scope | Decision-First Default-Visible Content | Operator Diagnostics | Support / Raw Evidence | One Dominant Next Action | Hidden / Gated By Default | Duplicate-Truth Prevention |
|---|---|---|---|---|---|---|---|
| Provider connection resource family | operator-MSP, support-platform | target scope, consent, verification, readiness summary | nested provider identity details and provider capability summary | raw secrets never appear; provider-owned identifiers stay secondary | existing primary provider action for the current surface | provider-owned detail stays nested and credential data stays hidden | one shared target-scope summary remains the primary truth across list/detail/edit |
| Verification report viewer / operations detail | operator-MSP, support-platform, readonly-entitled members | overall summary, counts, and next steps from stored report data | per-check status, reason code, and evidence pointers | raw provider payloads remain out of scope and unavailable by design | `View run` or existing report follow-through | raw payloads remain absent; support detail stays nested if already available elsewhere | the report summary states the outcome once and later sections add evidence rather than duplicating the same blocker text |
## UI/UX Surface Classification *(mandatory when operator-facing surfaces are changed)*
| Surface | Action Surface Class | Surface Type | Likely Next Operator Action | Primary Inspect/Open Model | Row Click | Secondary Actions Placement | Destructive Actions Placement | Canonical Collection Route | Canonical Detail Route | Scope Signals | Canonical Noun | Critical Truth Visible by Default | Exception Type / Justification |
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| Provider connection resource family | List / Detail / Integrations | CRUD / List-first Resource | Open the connection or start the existing provider action appropriate to the row/detail surface | full-row inspect on list; dedicated detail page for record truth | required on list | grouped secondary actions remain in the existing `More` or header placement | unchanged existing destructive actions only | `/admin/provider-connections` | `/admin/provider-connections/{record}` | workspace, managed environment, provider, target scope | Provider connection | target scope plus consent and verification state | none |
| Verification report viewer / operations detail | Detail / Evidence / Follow-through | Shared detail surface | Review the latest verification outcome and next steps | explicit open of the existing operations or report detail | forbidden | existing secondary links only | no new destructive action | inherited current collection or follow-through entry point | canonical `admin.operations.view` or embedded existing report viewer | workspace, managed environment, run identity | Verification report | overall summary and counts | none |
## Operator Surface Contract *(mandatory when operator-facing surfaces are changed)*
| Surface | Primary Persona | Decision / Operator Action Supported | Surface Type | Primary Operator Question | Default-visible Information | Diagnostics-only Information | Status Dimensions Used | Mutation Scope | Primary Actions | Dangerous Actions |
|---|---|---|---|---|---|---|---|---|---|---|
| Provider connection resource family | Workspace operator | Decide whether the connection is the right binding and whether provider work can start now | List/detail resource | Is this the right connection, and is it startable or follow-up-worthy right now? | target scope, consent, verification, readiness summary | nested provider identity details, capability summary, and recent run continuity | consent, verification, readiness | existing provider actions only | Open connection, Check connection, Inventory sync, Compliance snapshot | unchanged existing lifecycle or credential mutations only |
| Verification report viewer / operations detail | Workspace operator or readonly-entitled member | Understand what the most recent verification run proved and what to do next | Shared report/detail surface | What did the last verification actually prove? | overall summary, counts, and next steps | per-check reason codes and evidence pointers | verification outcome and per-check status | none on the report viewer itself | View run or existing follow-through link | none added by this slice |
## UI Action Matrix *(mandatory when Filament is changed)*
`294` does not add a new action family. The matrix below pins the existing operator-facing action surfaces whose disclosure and runtime semantics are being stabilized.
| Surface | Location | Header Actions | Inspect Affordance (List/Table) | Row Actions (max 2 visible) | Bulk Actions (grouped) | Empty-State CTA(s) | View Header Actions | Create/Edit Save+Cancel | Audit log? | Notes / Exemptions |
|---|---|---|---|---|---|---|---|---|---|---|
| Provider connections list | `/admin/provider-connections` | Existing `New connection` header action remains; no new header action is added by `294` | Clickable row to provider connection detail | Existing row `More` actions remain secondary; no new visible primary row mutation is introduced | none added by `294` | Existing `New connection` empty-state flow remains | n/a | n/a | existing only | `294` stabilizes disclosure and start-result truth only. The list keeps neutral target-scope visibility and existing provider-action affordances. |
| Provider connection detail | `/admin/provider-connections/{record}` | No new header action labels | Dedicated detail page | n/a | none | n/a | Existing grouped provider actions `Edit`, `Check connection`, `Inventory sync`, and `Compliance snapshot` remain secondary and unchanged in count | n/a | existing only | The detail page may change default-visible disclosure, but `294` does not introduce a new detail action surface or a new destructive action. |
| Provider connection edit | `/admin/provider-connections/{record}/edit` | No new header action labels | Dedicated edit page | n/a | none | n/a | Existing grouped secondary actions remain secondary | Existing Save and Cancel remain unchanged | existing only | `294` may stabilize supporting disclosure around the edit form, but does not change the edit-page action contract. |
| Verification report viewer / operations detail | Canonical `admin.operations.view` follow-through for verification report detail | none added by `294` | Dedicated detail page or existing follow-through viewer | n/a | none | n/a | Existing `View run` or equivalent follow-through remains the primary inspect action; no new mutation is introduced | n/a | existing only | Report disclosure may change through summary truth, but this slice does not add report-header mutations, bulk actions, or destructive actions. |
## Proportionality Review *(mandatory when structural complexity is introduced)*
- **New source of truth?**: no
- **New persisted entity/table/artifact?**: no runtime persistence; one spec-local `failure-classification.md` artifact only
- **New abstraction?**: no
- **New enum/state/reason family?**: yes, one spec-local failure-classification vocabulary for the implementation workflow only
- **New cross-domain UI framework/taxonomy?**: no
- **Current operator problem**: provider connection and verification flows currently disagree on what should start, dedupe, block, or count as the current report baseline, which makes the feature lane red and teaches the wrong runtime contract to maintainers and operators.
- **Existing structure is insufficient because**: the repo already has the runtime owners, but without one bounded stabilization package the remaining seam drift would be fixed piecemeal across tests and app code without a single shared contract.
- **Narrowest correct implementation**: classify the failing groups first, then adjust only the existing provider/verification owner seams and target lane tests until the current shared contract is explicit and green.
- **Ownership cost**: low to moderate; maintain one spec-local failure classification artifact and keep the same category and seam names aligned across the prep package.
- **Alternative intentionally rejected**: folding the work back into Spec `293`, reopening older historical specs, or widening to a full-suite repair. All would increase scope without improving the bounded provider/verification contract itself.
- **Release truth**: current-release runtime and test-governance stabilization only
### Compatibility posture
This feature assumes a pre-production environment.
Backward compatibility, legacy aliases, migration shims, and compatibility-only tests are out of scope. Canonical replacement remains preferred over preservation.
## Testing / Lane / Runtime Impact *(mandatory for runtime behavior changes)*
- **Test purpose / classification**: Feature, with conditional Browser reuse only if visible provider-connection disclosure changes
- **Validation lane(s)**: confidence and browser only when needed
- **Why this classification and these lanes are sufficient**: the package is explicitly bounded to `tests/Feature/ProviderConnections` and `tests/Feature/Verification`. Those tests already expose the remaining runtime seams honestly. One existing browser smoke may be reused only when implementation changes visible provider-connection disclosure on a real surface.
- **New or expanded test families**: none new by design; reuse the existing ProviderConnections and Verification feature files plus one existing provider-connection scope browser smoke if the implementation touches visible provider-connection disclosure
- **Fixture / helper cost impact**: moderate only because the package must stabilize the shared startable-fixture contract and avoid making expensive provider context setup implicit in more tests
- **Heavy-family visibility / justification**: no new heavy-governance family and no new browser family are justified
- **Special surface test profile**: `standard-native-filament`, `shared-detail-family`
- **Standard-native relief or required special coverage**: the feature lane is primary proof; conditional existing browser smoke is enough when visible provider-scope or report disclosure changes
- **Reviewer handoff**: reviewers must confirm that Filament remains on Livewire v4, provider registration stays in `apps/platform/bootstrap/providers.php`, no new global-search surface is introduced, existing destructive actions keep confirmation and server authorization, and the final proving commands stay inside the targeted provider/verification lane unless visible surface changes justify the one named browser smoke
- **Budget / baseline / trend impact**: contained feature-local upkeep only
- **Escalation needed**: `document-in-feature`
- **Active feature PR close-out entry**: `RuntimeSemantics`
- **Planned validation commands**:
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Verification/VerificationAuthorizationTest.php tests/Feature/Verification/VerificationStartAfterCompletionTest.php tests/Feature/Verification/VerificationStartDedupeTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderDispatchGateStartSurfaceTest.php tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php tests/Feature/Verification/ProviderConnectionHealthCheckWritesReportTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Browser/Spec281ProviderConnectionScopeSmokeTest.php`
- `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`
## Filament v5 / Surface Notes
- **Livewire v4.0+ compliance**: all touched admin surfaces remain on Filament v5 with Livewire v4
- **Provider registration location**: unchanged in `apps/platform/bootstrap/providers.php`
- **Global search rule**: `ProviderConnectionResource` remains non-globally-searchable, and this slice does not introduce a new globally-searchable resource
- **Destructive actions**: no new destructive action is introduced; any touched existing mutation must retain `->action(...)`, `->requiresConfirmation()`, and server-side authorization
- **Asset strategy**: no new assets or `filament:assets` deployment changes are introduced
## Scope Boundaries *(required for this slice)*
### In Scope
- classify the current failing ProviderConnections/Verification groups into one bounded failure vocabulary before any implementation work begins
- align the shared startable verification fixture contract with the current canonical repo truth for queued verification starts
- stabilize `StartVerification` so canonical startable connections return `started`, `deduped`, or a fresh post-completion run while blocked remains reserved for real prerequisite failures
- stabilize `ProviderOperationStartGate` so identical-operation dedupe and cross-operation `scopeBusy` behavior remain consistent for provider-backed operations
- stabilize the provider connection neutrality baseline so target-scope disclosure stays neutral and provider-specific identity detail remains nested intentionally
- stabilize verification report summary counts so tests follow the current emitted check inventory instead of stale literal limits
- keep the validation lane bounded to `tests/Feature/ProviderConnections` and `tests/Feature/Verification`, with conditional reuse of one existing browser smoke only when visible provider-scope or report disclosure changes
### Non-Goals
- reopening any `/admin/t/...` or workspace-route cutover repair
- full-suite repair outside `tests/Feature/ProviderConnections` and `tests/Feature/Verification`
- reopening historical package work from Specs `238`, `188`, `084`, or `074`
- introducing a new provider-profile table, provider registry, capability registry, or verification schema version
- broad provider-boundary or multi-provider framework work
- new surfaces, new panels, or new notification families
## User Scenarios & Testing *(mandatory)*
### User Story 1 - Start verification on one canonical contract (Priority: P1)
As an operator with provider-run capability, I can start provider verification on a canonical startable connection and get truthful `started`, `deduped`, or fresh post-completion behavior instead of an unexpected blocked result caused by stale fixture or runtime drift.
**Why this priority**: this is the operator-critical entry point, and the current failing lane already proves that the start contract is the most user-visible remaining drift.
**Independent Test**: Can be fully tested by running `VerificationAuthorizationTest`, `VerificationStartAfterCompletionTest`, and `VerificationStartDedupeTest` against canonical startable fixtures and confirming those tests no longer fall back to blocked semantics.
**Acceptance Scenarios**:
1. **Given** a managed environment member with provider-run capability and a canonical startable provider connection, **When** verification starts, **Then** the result is `started` and a `provider.connection.check` run is created.
2. **Given** the same canonical connection already has an active verification run, **When** verification starts again, **Then** the result is `deduped` and points to the same active run.
3. **Given** the previous verification run is terminal, **When** verification starts again, **Then** a new run is created instead of returning the prior run or a blocked result.
---
### User Story 2 - Keep provider operation concurrency truthful (Priority: P1)
As an operator, I can trust that provider operations for the same connection either dedupe the same operation or return one shared `scopeBusy` follow-through for a different active operation, without creating contradictory extra runs.
**Why this priority**: provider verification and provider-backed operation trust share the same gate. If concurrency semantics drift, the provider lane stays red and the operator contract becomes untrustworthy.
**Independent Test**: Can be fully tested by running `ProviderDispatchGateStartSurfaceTest` and `ProviderOperationConcurrencyTest` and confirming no extra operation runs or missing queue pushes occur once the current gate contract is aligned.
**Acceptance Scenarios**:
1. **Given** an inventory sync is already active for a connection, **When** a compliance snapshot starts for that same connection, **Then** no second run is created and the result surfaces the existing active run as `scopeBusy` guidance.
2. **Given** the same provider operation starts twice for the same connection while active, **When** the second start executes, **Then** the shared gate dedupes instead of creating a duplicate run.
---
### User Story 3 - Surface and report baselines stay on current truth (Priority: P2)
As an operator or reviewer, I can open provider connection detail and verification report surfaces and see the current neutral disclosure and report summary truth instead of stale copy or stale count ceilings.
**Why this priority**: these are the remaining visible baseline drifts after the deeper route cutover is complete.
**Independent Test**: Can be fully tested by running `ProviderConnectionNeutralitySpec238Test` and `ProviderConnectionHealthCheckWritesReportTest`. If provider-connection disclosure changes visibly, conditionally reuse the existing provider-connection scope browser smoke. Verification-report disclosure remains proven by the feature lane in `294`.
**Acceptance Scenarios**:
1. **Given** a provider connection detail surface shows target scope and nested provider identity detail, **When** the operator opens the page, **Then** the page shows the neutral target-scope baseline and the intended provider-specific nested detail without reverting to stale copy expectations.
2. **Given** a verification run writes the current report shape, **When** the report summary renders, **Then** `summary.counts.total` matches the emitted check inventory and tests do not assert an outdated hard-coded ceiling.
### Edge Cases
- A hand-built provider connection that looks valid in a test but does not meet the repo's canonical startable fixture contract must fail as blocked only when the canonical prerequisites are actually absent.
- An active queued run that has become stale must not make later verification or provider-operation starts look like a dedupe success when the canonical gate should clear it first.
- A provider connection surface may still show nested Microsoft tenant detail, but that detail must remain provider-owned context rather than replace the neutral target-scope summary.
- A new verification check may widen the report inventory; summary-count assertions must follow the canonical emitted inventory rather than a stale maximum.
- Readonly or non-member access semantics must remain unchanged while the start contract is stabilized: non-members stay `404`, in-scope actors missing capability stay `403`, and readonly users can view stored reports without starting verification.
## Non-Functional Requirements
- **NFR-001 (bounded proof lane)**: the primary proof command remains `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification`; no new broad lane becomes mandatory for this slice
- **NFR-002 (no new runtime truth)**: the slice must not introduce new tables, new persisted provider profiles, new run status values, or a new verification schema version
- **NFR-003 (shared-owner discipline)**: implementation must reuse existing runtime owners (`StartVerification`, `ProviderOperationStartGate`, `OperationRunService`, `ProviderConnectionSurfaceSummary`, `VerificationReportSchema`) instead of adding a second semantics layer
- **NFR-004 (conditional browser proof only)**: browser validation is conditional and reuses one existing provider-connection scope smoke only when visible provider-connection disclosure changes
## Requirements *(mandatory)*
**Constitution alignment (required):** This package introduces no new Graph integration surface, no new provider implementation, no new asset family, and no new long-running workflow beyond the current provider-operation and verification start paths. It stabilizes existing start/report semantics only.
**Constitution alignment (RBAC-UX):** This package preserves existing verification and provider-operation authorization semantics:
- non-member or wrong-scope access remains `404`
- in-scope actor missing capability remains `403`
- provider and verification starts continue to enforce capability server-side
- no raw capability string or role-string shortcut is introduced
**Constitution alignment (PROV-001):** Shared target-scope and verification summary semantics stay provider-neutral by default, while Microsoft-specific identity detail remains nested provider-owned context.
**Constitution alignment (TEST-GOV-001):** The package keeps proof in the narrowest honest feature lane and documents one optional existing browser smoke instead of adding a new heavy-governance or browser family.
For this package, a **failing group** means one failing test file or one failing scenario cluster that shares one seam and one category and is tracked as one row in `failure-classification.md`.
### Functional Requirements
- **FR-294-001 (failure classification first)**: Before implementation begins, every currently failing group in the targeted ProviderConnections/Verification lane must be recorded in `failure-classification.md` with exactly one pinned seam and one pinned failure category.
- **FR-294-002 (canonical startable fixture contract)**: Verification tests or start surfaces that expect a queued `provider.connection.check` run must use the repo's canonical startable fixture contract rather than ad hoc provider connection construction that no longer satisfies the current gate.
- **FR-294-003 (verification start semantics)**: `StartVerification` must return `started` for canonical startable connections, `deduped` for the same active verification identity, and a new run after a prior terminal run. `blocked` remains reserved for real prerequisite failures.
- **FR-294-004 (provider operation concurrency semantics)**: `ProviderOperationStartGate` must dedupe identical active operations for the same provider connection and return shared `scopeBusy` behavior for a different active provider operation on that same connection, without creating contradictory extra runs.
- **FR-294-005 (neutrality surface baseline)**: Provider connection edit, list, and detail expectations touched by this slice must keep neutral target-scope disclosure as the default visible truth and keep provider-specific identity details nested intentionally rather than as the top-level label set.
- **FR-294-006 (verification report summary baseline)**: Verification report summary counts must remain schema-valid, must match the emitted check inventory, and must not depend on stale hard-coded ceilings that no longer reflect the current check set.
- **FR-294-007 (bounded validation)**: The final implementation proof must stay bounded to the targeted ProviderConnections/Verification lane, plus conditional reuse of one existing provider-connection scope browser smoke only when visible provider-connection disclosure changes.
- **FR-294-008 (no spillover)**: The implementation must not absorb route-cutover fixes, full-suite cleanup, new provider-boundary groundwork, or historical-spec rewrites under the label of provider/verification stabilization.
## Success Criteria
- The targeted command `cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification` passes with no remaining failures in the in-scope lane.
- `failure-classification.md`, `spec.md`, `plan.md`, `research.md`, `data-model.md`, `quickstart.md`, `tasks.md`, and `checklists/requirements.md` all use the same pinned failure categories and seam names.
- No implementation change under `294` reopens `/admin/t/...` cutover work, adds a new provider abstraction, or widens into a broader full-suite repair.
- If visible provider-connection disclosure changes, the reused existing provider-connection scope browser smoke remains green.
## Assumptions
- Spec `293` already stabilized the route, panel, and workspace-aware baseline and remains context only.
- The repo's current canonical startable verification fixture is `createUserWithTenant(..., fixtureProfile: 'credential-enabled')` or an equivalent helper path that seeds a startable Microsoft provider connection truthfully.
- `ProviderConnectionSurfaceSummary` and the current target-scope normalizer remain the right owner seams for neutral provider-connection disclosure.
- `VerificationReportSchema` stays at the current major version, and this slice only stabilizes how current report inventory is asserted and surfaced.
- The active automatic candidate queue in `docs/product/spec-candidates.md` remains intentionally empty; `294` is a deliberate manual follow-up.
## Risks
- Misclassifying a fixture-contract drift as a runtime regression could cause unnecessary app changes in the provider or verification owners.
- Dedupe semantics may ultimately be owned one layer deeper than `ProviderOperationStartGate`; tasks must let implementation step one owner-hop lower only if targeted reruns prove that is necessary.
- Provider-connection view copy and shared summary output could drift again if tests are updated without aligning the shared summary owner.
- Verification report totals may widen again when future checks are added; tasks must tie test expectations to the canonical emitted inventory rather than another literal cap.
## Open Questions
- None at prep time. The confirmed target lane, current failing output, and directly read owner seams are sufficient to proceed to implementation.

View File

@ -0,0 +1,102 @@
# Tasks: Provider Verification Runtime Semantics Stabilization
**Input**: Design documents from `specs/294-provider-verification-runtime-semantics/`
**Prerequisites**: `spec.md`, `plan.md`, `research.md`, `data-model.md`, `quickstart.md`, `failure-classification.md`, `checklists/requirements.md`
**Review Artifact**: `specs/294-provider-verification-runtime-semantics/checklists/requirements.md`
**Failure Inventory**: `specs/294-provider-verification-runtime-semantics/failure-classification.md`
## Review Metadata
- **Review outcome class**: `acceptable-special-case`
- **Workflow outcome**: `keep`
- **Test-governance outcome**: `keep`
- **Stop / split triggers**: route-cutover repair, full-suite repair outside the target lane, new tables, persisted provider profiles, new run status values, new provider abstractions, new verification schema version, or historical-spec rewrites
## Pinned Failure-Classification Categories
- `surface-or-report-baseline-drift`
- `fixture-contract-drift`
- `provider-verification-runtime-regression`
- `dedupe-concurrency-contract-drift`
- `out-of-scope-existing-debt`
- `resolved-or-not-needed`
## Pinned Stabilization Seams
- `provider-neutrality-surface`
- `shared-startable-fixtures`
- `verification-start-contract`
- `provider-dispatch-concurrency`
- `verification-report-summary`
## Grouping Unit
In `294`, one failure group equals one failing test file or one failing scenario cluster that shares one seam and one category. The authoritative grouped baseline lives in `failure-classification.md`.
## Test Governance Checklist
- [x] The primary proof command is the bounded ProviderConnections/Verification feature lane.
- [x] Focused reruns are defined for verification start, provider dispatch/concurrency, and surface/report baseline clusters.
- [x] The existing provider-connection scope browser smoke is conditional and reused only if visible provider-connection disclosure changes.
- [x] No new browser family or heavy-governance lane is required.
- [x] Formatting is closed with `./vendor/bin/sail bin pint --dirty --format agent`.
## Phase 1: Setup and Classification
- [x] T001 Review `spec.md`, `plan.md`, `research.md`, `data-model.md`, `quickstart.md`, `failure-classification.md`, and `checklists/requirements.md` before touching runtime or tests; confirm Spec `293` remains context only.
- [x] T002 [P] Re-run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification`; the grouped prep-time baseline remained unchanged before implementation, and the final in-scope rerun closed green at `109 passed`.
- [x] T003 [P] Confirm each failing group still maps to exactly one pinned seam and one pinned category before the first code change.
## Phase 2: User Story 1 - Verification start contract (Priority: P1)
**Goal**: canonical startable verification fixtures and runtime owners agree on `started`, `deduped`, fresh post-completion reruns, and real blocked semantics.
**Independent Proof**:
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/Verification/VerificationAuthorizationTest.php tests/Feature/Verification/VerificationStartAfterCompletionTest.php tests/Feature/Verification/VerificationStartDedupeTest.php
```
- [x] T004 [P] Update `apps/platform/tests/Feature/Verification/VerificationAuthorizationTest.php` so the started path uses a canonical startable fixture and any intentionally blocked case stays explicit.
- [x] T005 [P] Update `apps/platform/tests/Feature/Verification/VerificationStartAfterCompletionTest.php` and `apps/platform/tests/Feature/Verification/VerificationStartDedupeTest.php` to pin fresh-run-after-completion and same-run dedupe semantics against canonical fixture data.
- [x] T006 Verify `apps/platform/tests/Pest.php` remains the canonical source of startable verification truth and update the touched verification fixture call sites to use `createUserWithTenant(..., fixtureProfile: 'credential-enabled')`.
- [x] T007 Focused reruns proved the canonical verification runtime owner already matched repo truth, so no `StartVerification` runtime change was needed.
## Phase 3: User Story 2 - Provider-operation concurrency (Priority: P1)
**Goal**: same-operation reruns dedupe, different active operations on the same provider connection return `scopeBusy`, and queue/run counts stay gate-owned.
**Independent Proof**:
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderDispatchGateStartSurfaceTest.php tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php
```
- [x] T008 [P] Update `apps/platform/tests/Feature/ProviderConnections/ProviderDispatchGateStartSurfaceTest.php` to pin the current cross-operation `scopeBusy` follow-through and queue-count contract.
- [x] T009 [P] Update `apps/platform/tests/Feature/ProviderConnections/ProviderOperationConcurrencyTest.php` to pin same-operation dedupe, cross-operation `scopeBusy`, and run-identity expectations after fixture alignment.
- [x] T010 Align the provider-operation fixture prerequisites instead of widening runtime code; focused reruns proved `ProviderOperationStartGate` and `OperationRunService` did not need changes once capability evidence existed.
## Phase 4: User Story 3 - Surface and report baselines (Priority: P2)
**Goal**: provider connection disclosure and verification report summaries match the current shared owner truth.
**Independent Proof**:
```bash
export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php tests/Feature/Verification/ProviderConnectionHealthCheckWritesReportTest.php
```
- [x] T011 [P] Update `apps/platform/tests/Feature/ProviderConnections/ProviderConnectionNeutralitySpec238Test.php` so the assertions pin the current neutral target-scope disclosure and nested provider identity detail baseline without reintroducing `Entra`-only wording.
- [x] T012 [P] Update `apps/platform/tests/Feature/Verification/ProviderConnectionHealthCheckWritesReportTest.php` so `summary.counts.total` follows the current emitted check inventory instead of a stale literal ceiling.
- [x] T013 Focused reruns proved the current surface/report producers were already correct, so no runtime change was needed in the provider target-scope or verification report seams.
## Phase 5: Cross-Cutting Validation and Close-Out
- [x] T014 Run the focused verification-start proof command from Phase 2 until it passes.
- [x] T015 Run the focused provider-dispatch proof command from Phase 3 until it passes.
- [x] T016 Run the focused surface/report proof command from Phase 4 until it passes.
- [x] T017 Run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail artisan test --compact tests/Feature/ProviderConnections tests/Feature/Verification` and confirm the full in-scope lane is green.
- [x] T018 [P] Runtime provider-connection disclosure did not change in application code, so the existing browser smoke was not required for this implementation.
- [x] T019 Run `export PATH="/bin:/usr/bin:/usr/local/bin:$PATH" && cd apps/platform && ./vendor/bin/sail bin pint --dirty --format agent`.
- [x] T020 Review `failure-classification.md` and `checklists/requirements.md` one last time to confirm no route-cutover, full-suite, table/profile/run-status, provider-framework, verification-schema-version, or historical-spec-rewrite work slipped into `294`, and classify any residual mismatch instead of widening scope silently.