91 lines
4.2 KiB
Markdown
91 lines
4.2 KiB
Markdown
|
|
# Contract: `Invoke-PersonaEngine.ps1`
|
||
|
|
|
||
|
|
The engine entry point. Retrieval, evaluation, reporting, and controlled persistence.
|
||
|
|
|
||
|
|
## Signature
|
||
|
|
|
||
|
|
```powershell
|
||
|
|
[CmdletBinding(SupportsShouldProcess = $true, ConfirmImpact = 'High')]
|
||
|
|
param(
|
||
|
|
[Parameter(Mandatory)][string] $ConfigPath,
|
||
|
|
[Parameter()][guid] $UserObjectId,
|
||
|
|
[Parameter()][string] $OutputPath,
|
||
|
|
[Parameter()][guid] $CorrelationId
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
`SupportsShouldProcess` supplies `-WhatIf` and `-Confirm`. `-Verbose` and `-Debug` are common
|
||
|
|
parameters and are **not** declared.
|
||
|
|
|
||
|
|
## Parameter contract
|
||
|
|
|
||
|
|
| Parameter | Required | Behaviour |
|
||
|
|
| --- | --- | --- |
|
||
|
|
| `-ConfigPath` | Yes | Path to the JSON configuration. Validated through all four layers before any connection is made (FR-002). |
|
||
|
|
| `-WhatIf` | No | **The approved no-write control.** Reads, evaluation, comparison, console output, summaries, and audit records all behave identically to enforcement; zero write requests are issued (FR-017, SC-004). |
|
||
|
|
| `-UserObjectId` | No | Single-user execution for validation. Skips enumeration; retrieves one user. |
|
||
|
|
| `-OutputPath` | No | Overrides the configured audit output path where permitted. |
|
||
|
|
| `-CorrelationId` | No | Supplied run identifier. Generated when absent. Appears on every audit record. |
|
||
|
|
| `-Verbose` | No | Operational detail. **MUST NOT** alter write behaviour. |
|
||
|
|
| `-Debug` | No | Enables condition-value tracing (Principle V). **MUST NOT** imply read-only — a `-Debug` run without `-WhatIf` writes. |
|
||
|
|
|
||
|
|
## Mode determination
|
||
|
|
|
||
|
|
```text
|
||
|
|
$PSCmdlet.ShouldProcess() returns $false -> Preview mode -> no write request constructed or sent
|
||
|
|
$PSCmdlet.ShouldProcess() returns $true -> Enforce mode -> write permitted, subject to FR-016
|
||
|
|
```
|
||
|
|
|
||
|
|
Mode MUST be derived from `ShouldProcess` alone. A separate boolean "preview" flag is prohibited —
|
||
|
|
two sources of truth for the write gate is precisely the defect class Principle III exists to
|
||
|
|
prevent.
|
||
|
|
|
||
|
|
## Write gate (FR-016)
|
||
|
|
|
||
|
|
A write is issued only when **all** hold:
|
||
|
|
|
||
|
|
1. Evaluation completed successfully (`Outcome != EvaluationError`).
|
||
|
|
2. `CalculatedPersona != StoredPersona` (ordinal comparison, case-sensitive for change detection).
|
||
|
|
3. The target attribute is non-blank and present in `approvedWritableAttributes`.
|
||
|
|
4. `ShouldProcess` returned `$true` for this user.
|
||
|
|
|
||
|
|
Failing any of these yields `Unchanged`, `WouldUpdate`, or `Skipped` — never a silent write.
|
||
|
|
|
||
|
|
## Output contract
|
||
|
|
|
||
|
|
- **Per user, immediately after evaluation** (FR-018, SC-012): one console line carrying UPN,
|
||
|
|
Account Object ID, outcome, matched rule ID, stored value, calculated value, and action.
|
||
|
|
- **Every `summaryInterval` users** (FR-019): a table of all business rules with match counts, plus
|
||
|
|
outcome totals and a reconciliation check.
|
||
|
|
- **At completion**: a final summary regardless of interval, including when the interval is `0`
|
||
|
|
(FR-020).
|
||
|
|
- **Reconciliation** at every summary: `Processed = Matched + Unclassified + EvaluationError`
|
||
|
|
(FR-021). A mismatch is logged as an engine defect, at `Error` severity.
|
||
|
|
- **Audit records**: see [audit-record.md](audit-record.md).
|
||
|
|
|
||
|
|
## Exit codes
|
||
|
|
|
||
|
|
| Code | Condition |
|
||
|
|
| --- | --- |
|
||
|
|
| `0` | Successful run; no fatal processing errors |
|
||
|
|
| `1` | Configuration validation failure |
|
||
|
|
| `2` | Authentication / authorization failure |
|
||
|
|
| `3` | User enumeration failure |
|
||
|
|
| `4` | Fatal required data-provider failure |
|
||
|
|
| `5` | Reconciliation failure |
|
||
|
|
| `6` | Unexpected fatal engine error |
|
||
|
|
|
||
|
|
Every code MUST be reachable and returned for its documented condition (SC-011). A per-user
|
||
|
|
`EvaluationError` does **not** by itself terminate the run; the final status reports the affected
|
||
|
|
count and applies `evaluationErrorThreshold` when configured.
|
||
|
|
|
||
|
|
## Invariants (test-asserted)
|
||
|
|
|
||
|
|
| Invariant | Assertion |
|
||
|
|
| --- | --- |
|
||
|
|
| Zero writes under `-WhatIf` | The write adapter is mocked; call count is `0` over a full synthetic population (SC-004) |
|
||
|
|
| Single-attribute body | Every captured request body has exactly one key, equal to `engine.targetAttribute` (SC-005) |
|
||
|
|
| Idempotence | Second consecutive run over unchanged input issues zero writes (SC-002) |
|
||
|
|
| Determinism | Same fixture set, shuffled input order, identical results (SC-003) |
|
||
|
|
| Exactly one outcome | Every processed user appears in exactly one outcome bucket (SC-001) |
|