TenantAtlas/specs/074-verification-checklist/quickstart.md
ahmido 439248ba15 feat: verification report framework (074) (#89)
Implements the 074 verification checklist framework.

Highlights:
- Versioned verification report contract stored in operation_runs.context.verification_report (DB-only viewer).
- Strict sanitizer/redaction (evidence pointers only; no tokens/headers/payloads) + schema validation.
- Centralized BADGE-001 semantics for check status, severity, and overall report outcome.
- Deterministic start (dedupe while active) via shared StartVerification service; capability-first authorization (non-member 404, member missing capability 403).
- Completion audit event (verification.completed) with redacted metadata.
- Integrations: OperationRun detail viewer, onboarding wizard verification step, provider connection start surfaces.

Tests:
- vendor/bin/sail artisan test --compact tests/Feature/Verification tests/Unit/Badges/VerificationBadgesTest.php
- vendor/bin/sail bin pint --dirty

Co-authored-by: Ahmed Darrazi <ahmeddarrazi@MacBookPro.fritz.box>
Reviewed-on: #89
2026-02-03 23:58:17 +00:00

2.9 KiB

Quickstart: Verification Checklist Framework (074)

This quickstart explains how to write and render a verification report attached to an OperationRun.

1) Writing a report (queued job / service)

Goal: produce a verification_report JSON document and store it in OperationRun->context.

Guidelines:

  • Generate reports inside queued execution (not in a Filament page render).
  • Keep evidence pointer-only (IDs/masked/hashes), never raw payloads or tokens.
  • Keep next steps navigation-only in v1.

Pseudo-code sketch:

$context = is_array($run->context) ? $run->context : [];

$context['verification_report'] = [
    'schema_version' => '1.0',
    'flow' => $run->type,
    'generated_at' => now('UTC')->toIso8601String(),
    'identity' => [
        'tenant_id' => (int) $run->tenant_id,
        'provider_connection_id' => (int) data_get($run->context, 'provider_connection_id', 0),
    ],
    'summary' => [
        'overall' => 'needs_attention',
        'counts' => [
            'total' => 5,
            'pass' => 3,
            'fail' => 2,
            'warn' => 0,
            'skip' => 0,
            'running' => 0,
        ],
    ],
    'checks' => [
        [
            'key' => 'provider_connection.token_acquisition',
            'title' => 'Token acquisition works',
            'status' => 'fail',
            'severity' => 'high',
            'blocking' => true,
            'reason_code' => 'permission_denied',
            'message' => 'The app cannot acquire a token with the configured credentials.',
            'evidence' => [
                ['kind' => 'provider_connection_id', 'value' => (int) data_get($run->context, 'provider_connection_id')],
            ],
            'next_steps' => [
                ['label' => 'Review connection credentials', 'url' => '/admin/...'],
                ['label' => 'Microsoft docs: app permissions', 'url' => 'https://learn.microsoft.com/...'],
            ],
        ],
    ],
];

$run->update(['context' => $context]);

2) Rendering the report (Filament, DB-only)

Recommended integration points:

  • Monitoring → Operations: in the OperationRun view page, show a “Verification report” section when context.verification_report exists.
  • Flow pages (e.g., onboarding wizard): embed the same viewer component using the run ID stored in wizard state.

Hard requirement: rendering must not trigger any outbound HTTP (no Graph calls, no jobs dispatched, no side effects).

3) Authorization split

  • Viewing a report: allowed for tenant-scoped members.
  • Starting verification: requires a specific capability.
  • Non-members: deny-as-not-found (404) for tenant-scoped pages and actions.

4) Tests to add

  • Viewer DB-only render test: Http::fake() + assert no requests during render.
  • Evidence redaction test: report JSON contains none of access_token, client_secret, Authorization, bearer tokens, or raw payload dumps.
  • Dedupe test: repeated starts while active reuse the same run.