What this engine is permitted to do, what it is not, and where the gap between those two is held
open by testing rather than by the platform.
## The central trade-off (OTD-003)
**Microsoft Graph application permissions have no per-property write scope.** An identity granted
`User.ReadWrite.All` can write *any* writable property on *any* user object. It cannot be narrowed to
one extension attribute.
This is not a limitation to be worked around. It is a fact about the platform, recorded here so that
nobody later assumes the directory is enforcing something it is not.
The consequence: **the only thing standing between this engine and every writable user property is
the code in this repository, and the tests that hold it to that.** Every control below exists because
the directory will not refuse a malformed request on our behalf.
## The six compensating controls
All six are mandatory. Each is testable, and each is tested.
| # | Control | Where it lives | Proof |
| --- | --- | --- | --- |
| 1 | The persistence layer accepts only the configured target attribute | `New-PersonaWriteBody` throws for any other name | `WriteBodyRejection.Tests.ps1` |
| 2 | The target must appear in `approvedWritableAttributes` | `Resolve-TargetAttribute` and `New-PersonaWriteBody`, checked twice | `WriteBodyRejection.Tests.ps1` |
| 3 | Validation rejects every other attribute | `PE-SAF-002`, layer 4 | `Safety.Tests.ps1` |
| 4 | One dedicated function builds the request body, and it is the only one | `New-PersonaWriteBody` returns a hashtable whose `Count` is exactly 1 | `WriteBody.Tests.ps1` |
| 5 | Tests inspect the captured request body | Every body issued during a full enforcing run is asserted to have one key | `WriteBody.Tests.ps1` |
| 6 | Code owners and branch policies gate persistence changes | Repository configuration, outside this codebase | Branch protection on `src/Persistence/` |
Control 4 is the load-bearing one. A single construction site makes SC-005 a property of one testable
function rather than a convention every future call site has to remember. `WriteBody.Tests.ps1`
includes a scan asserting that no other file under `src/` builds a PATCH body.
Control 2 is deliberately redundant. Validation runs once at startup against the file; the write
builder checks again on every write against the values actually in hand — so a configuration object
mutated mid-run still cannot widen the blast radius.
### Why comparison is ordinal here and case-insensitive elsewhere
Rule matching is case-insensitive (RE-006), because a rule author should not have to match directory
casing. Attribute approval is **ordinal and case-sensitive**, because extension property names are
case-sensitive in Graph: `extension_<id>_Persona` and `extension_<id>_persona` are two different
attributes, and approving one does not approve the other.
Change detection is also ordinal (FR-015). A stored `employee` against a calculated `Employee` is a
real difference worth correcting, not a formatting quirk.