# Issue 1129 Decision: Derived Recompute Profile-Global Contract

## Decision

`#1129` resolves to the marker-only contract.

Rollup-derived parent saves do **not** impersonate a real or formula-only
System Administrator profile. The runtime keeps `$User`, `$Profile`, and
`$UserRole` sourced from the behavior-bearing `UserSession` through the single
`formula.BuildFormulaUserContext` read path, and exposes platform-owned rollup
recomputes only through `$SystemContext.IsDerivedRecompute`.

That means pre-existing metadata that gates directly on `$Profile.Id`,
`$Profile.Name`, or `$User.ProfileId` does **not** continue to pass unchanged
during a derived recompute. Metadata that should allow the platform-owned save
must opt in explicitly by incorporating `$SystemContext.IsDerivedRecompute`.

## Why This Is The Chosen Contract

The rejected alternative was a hybrid projection: keep permission and
record-type behavior non-admin while making formula globals look like
`system_admin`. That shape is not stable in this repository.

It creates two competing answers to the same fact:

- behavior checks read `UserSession.ProfileID` / `IsSystemAdmin`
- formula gates read a different profile identity

Once those diverge, an ordinary child write can satisfy parent validation or
before-save flow branches that were authored as admin/profile gates during the
derived parent save lifecycle. That violates the repository rule of one fact,
one SSoT, and one read path for Salesforce-modeled identity data.

## Runtime Contract

The chosen contract on `origin/main` is:

- `RollupService.ProcessRollups` loads the synthetic internal actor through
  `PersistenceService.LoadSystemUserSession`.
- `LoadSystemUserSession` remains greenfield-safe and non-privileged: it does
  not require a persisted `User/system` row and does not stamp profile/admin
  identity onto the actor.
- `UpdateDerived` marks the context with
  `constants.WithFormulaDerivedRecompute(...)`.
- Validation rules and flow runtime both surface that marker as
  `$SystemContext.IsDerivedRecompute`.
- `formula.BuildFormulaUserContext` remains the only construction path for
  `$User`, `$Profile`, and `$UserRole`, and it projects only the fields already
  present on `UserSession`.

## Metadata Compatibility Rule

If a validation rule or flow condition is intended to allow platform-owned
rollup recomputes, it must use the marker explicitly. The canonical shape is:

```text
NOT($SystemContext.IsDerivedRecompute) && <existing business gate>
```

Examples:

- validation rule:

```text
NOT($SystemContext.IsDerivedRecompute) &&
$Profile.Id != "system_admin" &&
Amount > 0 &&
ISBLANK(Description)
```

- flow decision:

```text
$SystemContext.IsDerivedRecompute = false
```

The platform does not treat rollup-derived recomputes as “real admin writes”
for compatibility.

## Migration Implication

This is an explicit metadata-migration contract for first-party packs and
admin-authored metadata:

- existing profile-global rules that should allow derived recomputes must be
  updated to include `$SystemContext.IsDerivedRecompute`
- rules that should continue to block derived recomputes need no change

The migration belongs at the metadata layer, not by widening the L0 runtime
identity model.

## Verification

The regression surface required by `#1129` is:

- positive path: `ProcessRollups` succeeds when parent metadata uses
  `$SystemContext.IsDerivedRecompute`
- negative path: `ProcessRollups` still fails with a `ValidationRuleError`
  when parent metadata gates only on `$Profile.Id` or `$Profile.Name`

Focused coverage lives in:

- `backend/internal/features/records/persistence_rollup_errors_test.go`

This locks the issue resolution to the real child-write ->
`ProcessRollups` -> `UpdateDerived` path rather than a synthetic helper-only
unit.
