# Wave 8B3 — Provider Approval Bridge Validation

Date: 2026-08-04

## Scope

Wave 8B3 moves Provider approval/rejection ownership into the Accounts vertical
module. It is an architecture-only change. Dashboard routes, middleware,
controllers, validators, views, API documentation, schemas, notification copy,
and response envelopes remain unchanged.

## Canonical ownership

- Application orchestration:
  `src/modules/accounts/application/provider-approval.service.js`
- MongoDB persistence:
  `src/modules/accounts/infrastructure/provider-approval.repository.js`
- File staging and rollback:
  `src/modules/accounts/infrastructure/provider-approval-file-store.js`
- Contract constants:
  `src/modules/accounts/domain/provider-approval.constants.js`
- Pure transition, collision, mapping, and activation policy:
  `src/modules/accounts/domain/provider-approval.policy.js`
- Public boundary: `src/modules/accounts/index.js`

The old service and ProviderMeta contract paths remain thin compatibility
adapters. Runtime consumers therefore keep their existing imports and object
identities while the Accounts module owns the implementation.

## Frozen behavior

### Legacy ProviderMeta

ProviderMeta rows without `accountIdentity` continue to use the legacy
ProviderMeta/Provider shared-id transition. Both rows are conditionally changed
from `wait`; a failed Provider transition restores the request. No
AccountIdentity is created, migrated, linked, or repaired.

### AccountIdentity-backed acceptance

Acceptance requires an active, matching identity with the Provider role, the
same `providerRequest`, no `providerProfile`, and no linked/colliding Provider.
One Provider is created with an independent id and an explicit field allowlist.
Provider creation, ProviderMeta acceptance, and AccountIdentity linking are one
MongoDB transaction. Provider-only pending mode changes only under the existing
activation rule; a hybrid account keeps its current mode and normalized role
order.

### AccountIdentity-backed rejection

Rejection changes only an eligible pending ProviderMeta inside a transaction.
It does not create a Provider or stage files, and it preserves AccountIdentity
roles, links, activation, and mode.

## File lifecycle

Provider files are staged before the acceptance transaction at the same
lifecycle boundary as before. Missing source directories are a safe no-op.
Every path segment is validated. If the database transaction fails after a
copy, the new Provider target directory is removed; the original ProviderMeta
directory is not deleted.

## Controller boundary

Audit reports and notifications remain in the dashboard controller and run
only after a successful transition. Accepted notifications target the created
Provider (with the existing legacy fallback), rejected notifications target
ProviderMeta, and existing URLs/status/envelopes are unchanged.

## Evidence gates

- Compatibility identity and adapter purity:
  `test/providerApprovalArchitectureWave8B3.test.js`
- Legacy and AccountIdentity behavior, transactions, collisions, and rollback:
  `test/accountIdentityProviderApproval.test.js`
- Dashboard route, CSRF, validator, response, and side-effect contract:
  `test/dashboardProviderMetaContract.test.js`
- Wave 8B2 and frozen schema/OpenAPI/Auction hashes are asserted unchanged.

This wave performs no migration, backfill, auto-link, schema mutation, UI
change, Swagger/Postman change, password/token change, or global RBAC change.

## Final gate results

- Focused Provider approval and adjacent Accounts tests: **212/212 passed**.
- Full `npm test`: **1123/1123 passed**.
- Changed/untracked JavaScript `node --check`: **403/403 passed**.
- `git diff --check`: passed.
- Frozen pre-audit SHA-256 comparison: all 13 paths passed, including the
  Auction schema, five identity/profile schemas, dashboard route/controller/
  validator, OpenAPI, and all Wave 8B2 canonical role-add files.
- Dirty-worktree accountability and generated-artifact tests: passed.
