# `Profile` Full Gap Inventory

This document records the first full inventory pass for the remaining `Profile` umbrella problem after the first two repair tracks were merged.

## Scope

This pass does **not** introduce a code fix yet.

Its purpose is to answer the roadmap question:

> Which `Profile`-owned permission sets still have missing `ObjectPermissions` / `FieldPermissions`, and which layer should the next repair target?

Validation target:

- environment: `ci-test`
- inventory date: `2026-04-24`
- branch baseline: `origin/main` after `#1846` and `#1872`

## Prior Repairs Already Included

This inventory was taken **after** the first two merged repair tracks:

1. `validation-01-system-permission-ssot-backfill.md`
   - restored `PermissionSet.Permissions* -> _System_PermissionSetPermission`

2. `validation-02-setup-object-permission-repair.md`
   - restored metadata-gated setup-object `ObjectPermissions` / `FieldPermissions`

So the goal here is to identify what still remains **after** those fixes.

## Inventory Method

The inventory query model was:

- universe of permission sets: `PermissionSet.IsOwnedByProfile = 1 AND IsDeleted = 0`
- universe of objects: `_System_Object.IsDeleted = 0`
- universe of fields: `_System_Field.IsDeleted = 0`
- missing object pair:
  - no `ObjectPermissions` row for `(PermissionSetId, SobjectType)`
- missing field pair:
  - no `FieldPermissions` row for `(PermissionSetId, SobjectType, Field)`

To reduce noise for business-object analysis, a second pass excluded historical discovery leftovers with names matching `%_bak_%`.

## High-Level Result

### 1. The `validation-02` target class is now fully repaired

For setup objects whose access is metadata-gated by `_System_Object.RequiredWritePermission`:

| Slice | Missing `ObjectPermissions` pairs | Missing `FieldPermissions` pairs |
| --- | ---: | ---: |
| setup objects with non-blocked `RequiredWritePermission` | `0` | `0` |

This confirms the previous repair is working for the exact setup-object class it targeted.

### 2. The remaining umbrella problem is centered on non-setup / business objects

After excluding `%_bak_%` discovery leftovers:

| Slice | Missing object pairs | Affected objects | Affected profiles | Missing field pairs | Objects missing field rows | Profiles missing field rows |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| business objects (excluding `%_bak_%`) | `2512` | `82` | `39` | `127994` | `82` | `48` |

Interpretation:

- `82` business objects still have missing permission coverage
- `39` profile-owned permission sets are missing at least one business-object `ObjectPermissions` row
- all `48` profile-owned permission sets are missing at least one business-object `FieldPermissions` row

### 3. A raw setup-object inventory still shows broad gaps, but outside the repaired target slice

If all setup objects in `_System_Object` are counted without scoping:

| Slice | Missing object pairs | Affected objects | Affected profiles | Missing field pairs | Objects missing field rows | Profiles missing field rows |
| --- | ---: | ---: | ---: | ---: | ---: | ---: |
| all setup objects | `10197` | `266` | `48` | `174804` | `266` | `48` |

This should **not** be read as “`validation-02` failed.”

It means:

- the repaired slice is healthy
- there are many other setup objects that are not covered by the metadata-gated setup-object repair model
- those rows should not be mixed into the next business-object repair without clearer invariants

## Top Remaining Business Objects

Top missing business objects after excluding `%_bak_%` names:

| Object API Name | Profiles missing object rows | Missing field pairs |
| --- | ---: | ---: |
| `campaignmember` | `39` | `2796` |
| `contentversion` | `39` | `1872` |
| `emailmessage` | `39` | `1599` |
| `opportunitylineitem` | `39` | `1404` |
| `quotelineitem` | `39` | `1209` |
| `orderitem` | `39` | `1014` |
| `forecastingowneradjustment` | `39` | `975` |
| `FlowInterview` | `39` | `942` |
| `ProcessDefinition` | `39` | `864` |
| `apextrigger` | `39` | `858` |
| `contentnote` | `39` | `819` |
| `fiscalyearsettings` | `39` | `819` |
| `forecastingquota` | `39` | `819` |
| `lead_owner_allocation_rules__c` | `39` | `819` |
| `approvalworkitem` | `39` | `780` |

Key observation:

- the remaining gap is not isolated to one or two objects
- it is a broad, repeated pattern across dozens of business objects

## Top Affected Profiles

Representative affected profiles:

| Profile | Missing business objects | Missing business fields |
| --- | ---: | ---: |
| `Anypoint Integration` | `82` | `3223` |
| `Identity User` | `82` | `3223` |
| `Salesforce API Only System Integrations` | `82` | `3223` |
| `PoC Profile` | `81` | `3223` |
| `Partner Community User` | `77` | `3223` |
| `CPQ Integration User` | `74` | `3223` |
| `Analytics Cloud Security User` | `70` | `3223` |
| `Contract Manager` | `70` | `3223` |
| `Marketing User` | `70` | `3223` |
| `Read Only` | `70` | `3223` |
| `Read Only-IT` | `70` | `3223` |
| `Solution Manager` | `70` | `3223` |
| `Standard User (Salesforce)` | `70` | `3223` |
| `AI Agent Profile` | `66` | `3223` |
| `Management` | `55` | `3223` |

Key observation:

- this is not isolated to one migrated admin profile
- the drift spans standard users, integration users, community users, and custom business profiles

## Noise Identified During Inventory

The raw business-object inventory included discovered backup objects such as:

- `Profile_bak_20260401_1`
- `User_bak_20260401_1`
- `PermissionSetAssignment_bak_20260401_1`

These inflated the raw counts but are not the best primary target for the next repair.

For convergence planning, the cleaner signal is:

- business objects excluding `%_bak_%`

## Code-Path Inspection Result

After inventory, the most likely next repair layer is **control-plane seeding / reconciliation**, not the runtime create paths.

### What looks healthy

- `Profile` creation path already calls:
  - `EnsureProfilePermissionSet`
  - `SeedPermissionsForProfile`
- new object creation already calls:
  - `SeedPermissionsForObject`
- new field creation already calls:
  - `SeedFieldPermissionsForFields`

So newly created metadata does already have explicit permission provisioning hooks.

### What still looks too coarse

The current control-plane permission initialization still uses coarse seeded-state checks:

- `InitializePermissions` treats a profile as seeded once `ObjectPermissions` row count is greater than zero
- `reconcileHalfSeededProfiles` only repairs profiles where:
  - `ObjectPermissions > 0`
  - `FieldPermissions = 0`

That means these states still escape repair:

- partially seeded object coverage
- partially seeded field coverage
- profiles with some `FieldPermissions`, but not full schema coverage

This matches the inventory pattern much more closely than a runtime create-path bug.

## Conclusion

This inventory changes the next-step plan from “keep adding one-off repair slices” to:

1. treat `validation-01` and `validation-02` as confirmed, successful narrow repairs
2. treat the remaining `#1806` work as a **general partial-seeded business-object permission convergence problem**
3. target the next implementation at **forward correctness + partial-seeded reconciliation** in control-plane permission initialization

## Recommended Next Repair

The next repair should focus on:

- replacing profile-level `count > 0` seeded checks with per-object / per-field reconciliation
- backfilling missing business-object `ObjectPermissions`
- backfilling missing business-object `FieldPermissions`
- keeping the operation insert-only / idempotent so `setup apply` and `setup repair` can converge partial states safely

Suggested next artifact:

- `validation-04-business-object-permission-reconciliation.md`

