# Wave 8B.2 — Account Role-Add Vertical Ownership Validation

Date: 2026-08-04

## Scope

Wave 8B.2 moves the existing orchestration for these two authenticated API
operations into the Accounts vertical module:

- `POST /account/provider-request`
- `POST /account/client-role`

It does not change routes, middleware, validators, schemas, uploads, response
envelopes, tokens, provider approval, switch-mode, signup, migration, or UI.

## Canonical ownership

- Application orchestration:
  `src/modules/accounts/application/account-role.service.js`
- Persistence boundary:
  `src/modules/accounts/infrastructure/account-role.repository.js`
- Repeated role-add constants:
  `src/modules/accounts/domain/account-role.constants.js`
- Eligibility/collision policy:
  `src/modules/accounts/domain/account-role.policy.js`
- Public module entry: `src/modules/accounts/index.js`
- Compatibility adapter: `src/services/accountRoleService.js`

The compatibility path and Accounts public entry resolve to the same canonical
singleton, service class, error class, and error predicate.

## Frozen behavior proof

### Provider request

- Requires the linked active Client and AccountIdentity.
- Creates one pending `ProviderMeta` only.
- Never creates `Provider` or `AccountIdentity`.
- Copies identity-owned name, phone, country code, and email.
- Allow-lists provider business fields and does not persist password/token input.
- Adds the provider role, links `providerRequest`, keeps `providerProfile` null,
  and preserves client active mode.
- Detects linked Provider, pending request, cross-account Provider, and legacy
  ProviderMeta collisions without auto-linking.
- Admin notification delivery is invoked only after transaction commit and
  session close. A failed transaction never invokes the committed callback.
- Existing upload and cleanup handling remains in the Account API helper.

### Client role

- Requires the linked active Provider and AccountIdentity.
- Creates one Client only, with an independent Client identifier.
- Never creates `AccountIdentity`.
- Rejects existing/cross-account/legacy Client collisions without auto-linking.
- Preserves role order and the existing active-mode/accountMode result.
- Requires no password and returns no new token.

## Transaction and rollback proof

Focused harness coverage proves:

- successful role-add commits once and closes the session;
- ProviderMeta failure aborts, leaves identity roles/request unchanged, and
  closes the session;
- Client creation failure aborts, leaves identity roles/profile unchanged, and
  closes the session;
- duplicate-key races retain their existing conflict classifications;
- no notification callback runs for a failed ProviderMeta transaction.

## Contract preservation

The pre/post SHA-256 gate covers:

- Auction, AccountIdentity, Client, Provider, and ProviderMeta models;
- Account API route registration;
- Account request validation;
- public OpenAPI delivery JSON.

All hashes match the recorded Wave 8B.2 baseline. The frozen Auction hash is:

`ed06ce72c873bef1b32a47775f5d9acbb9f958ae2c9545481db4f842a946a04d`

## Automated gates

- Focused Wave 8B.2 and adjacent Accounts tests: **167/167 passed**.
- Full `npm test`: **1113/1113 passed**.
- Changed/untracked JavaScript `node --check`: **395/395 passed**.
- `git diff --check`: passed after the final full-suite run.
- New/untracked-file whitespace scan: passed.
- Dirty-worktree accountability: **3/3 passed**.
- Generated Postman metadata: clean.
- Screenshot artifacts: Git-ignored.
- Focused secret-pattern scan: no matches.

## Explicit exclusions

No migration, backfill, collision repair, legacy auto-link, provider approval,
password ownership, switch-mode, JWT/Passport, signup, Auction, dashboard, UI,
Swagger, or Postman behavior was added or changed in this wave.
