TenantAtlas/specs/203-baseline-compare-strategy/data-model.md
2026-04-13 23:09:11 +02:00

224 lines
10 KiB
Markdown

# Data Model: Baseline Compare Engine Strategy Extraction
## Overview
This feature introduces no new top-level persisted entity. It reuses the existing baseline compare start path, compare run persistence, finding lifecycle, and evidence storage, but inserts a new internal strategy boundary between platform compare orchestration and domain-specific compare logic.
## Existing Persisted Truth Reused Without Change
### Workspace-owned baseline truth
- `baseline_profiles`
- `baseline_snapshots`
- `baseline_snapshot_items`
- Canonical Baseline Scope V2 from Spec 202
These remain the reference truth that compare reads.
### Tenant-owned operational truth
- `operation_runs` for `baseline_compare`
- existing drift findings and recurrence tracking
- existing evidence and compare diagnostics stored in current finding and run payloads
These remain the long-lived operator truth written by compare.
### Existing subject and evidence inputs
- inventory-backed current state
- policy version and baseline snapshot evidence used by the current Intune path
- compare coverage and trust context already stored in the run and summary layers
This feature changes how domain logic is organized, not where those truths live.
## New Internal Contracts
### CompareStrategyKey
**Type**: string enum or equivalent value object
**Purpose**: identify one compare strategy family
| Value | Status | Notes |
|------|--------|-------|
| `intune_policy` | active | First explicit compare strategy family |
| future values | reserved | Additional families may be added later without changing the platform orchestration shape |
### CompareStrategyCapability
**Type**: derived registry record
**Purpose**: declare which canonical scope families a strategy supports
| Field | Type | Notes |
|------|------|-------|
| `strategy_key` | string | `CompareStrategyKey` value |
| `domain_keys` | array<string> | Supported governance domains |
| `subject_classes` | array<string> | Supported canonical subject classes |
| `subject_type_keys` | array<string> or `all` | Optional narrowing to known subject families |
| `compare_supported` | boolean | Whether compare may be started for the declared family |
| `active` | boolean | Whether the strategy is currently available |
### StrategySelectionState
**Type**: internal enum
**Purpose**: capture compare preflight compatibility outcome
| Value | Meaning |
|------|---------|
| `supported` | Exactly one compatible strategy family matches the requested scope |
| `unsupported` | No compatible strategy supports the requested scope |
| `mixed` | More than one strategy family would be required by the requested scope |
This is an orchestration contract, not a new top-level persisted domain status family.
### CompareStrategySelection
**Type**: internal orchestration record
**Purpose**: represent preflight strategy resolution for a compare start
| Field | Type | Notes |
|------|------|-------|
| `selection_state` | `StrategySelectionState` | Required |
| `strategy_key` | string or `null` | Present only when `selection_state = supported` |
| `matched_scope_entries` | array<object> | Canonical scope entries accepted by the selected strategy |
| `rejected_scope_entries` | array<object> | Scope entries rejected as unsupported or mixed |
| `operator_reason` | string | Short operator-safe explanation |
| `diagnostics` | array<string, mixed> | Secondary detail for logs and run detail |
### CompareOrchestrationContext
**Type**: internal execution record
**Purpose**: represent the platform-owned inputs to one compare run
| Field | Type | Notes |
|------|------|-------|
| `workspace_id` | integer | Required |
| `tenant_id` | integer | Required for tenant-owned compare runs |
| `baseline_profile_id` | integer | Required |
| `baseline_snapshot_id` | integer | Required reference snapshot |
| `operation_run_id` | integer | Required |
| `normalized_scope` | canonical scope document | Required |
| `strategy_selection` | `CompareStrategySelection` | Required |
| `coverage_context` | array<string, mixed> | Existing compare coverage truth |
| `launch_context` | array<string, mixed> | Existing start-surface context such as tenant or matrix origin |
### CompareSubjectIdentity
**Type**: internal value object
**Purpose**: represent one subject being compared without assuming policy-only semantics
| Field | Type | Notes |
|------|------|-------|
| `domain_key` | string | Canonical governance domain |
| `subject_class` | string | Canonical subject class |
| `subject_type_key` | string | Domain-owned subject family discriminator |
| `external_subject_id` | string | Stable external identifier where available |
| `subject_key` | string | Stable compare key used for deduplication and finding identity |
### CompareSubjectProjection
**Type**: internal value object
**Purpose**: provide the platform with operator-safe subject metadata for findings and summaries
| Field | Type | Notes |
|------|------|-------|
| `platform_subject_class` | string | Canonical class for platform writers |
| `domain_key` | string | Canonical governance domain |
| `subject_type_key` | string | Strategy-owned subject family |
| `operator_label` | string | Operator-facing subject label |
| `summary_kind` | string or `null` | Optional summary discriminator |
| `additional_labels` | array<string, string> | Optional secondary labels for diagnostics or detail surfaces |
### CompareState
**Type**: internal enum
**Purpose**: represent per-subject compare outcome
| Value | Meaning |
|------|---------|
| `no_drift` | Subject compared successfully and no drift was confirmed |
| `drift` | Subject compared successfully and drift was confirmed |
| `unsupported` | Subject or family is not supported by the selected strategy |
| `incomplete` | Evidence was insufficient to classify the subject fully |
| `ambiguous` | Identity or evidence ambiguity prevented a trustworthy compare decision |
| `failed` | Processing failed for this subject |
These values map to existing compare truth and do not imply a new top-level persisted run state family.
### CompareSubjectResult
**Type**: internal orchestration contract
**Purpose**: one structured output row from a compare strategy back to platform orchestration
| Field | Type | Notes |
|------|------|-------|
| `subject_identity` | `CompareSubjectIdentity` | Required |
| `projection` | `CompareSubjectProjection` | Required |
| `baseline_availability` | string | Required; e.g. `available`, `missing`, `not_applicable` |
| `current_state_availability` | string | Required; e.g. `available`, `missing`, `unknown` |
| `compare_state` | `CompareState` | Required |
| `trust_level` | string | Required; maps to current trust semantics |
| `evidence_quality` | string | Required; maps to existing evidence completeness semantics |
| `severity_recommendation` | string or `null` | Optional |
| `finding_candidate` | `CompareFindingCandidate` or `null` | Present when a finding should be written or updated |
| `diagnostics` | array<string, mixed> | Secondary detail for run context and troubleshooting |
### CompareFindingCandidate
**Type**: internal value object
**Purpose**: provide the unified finding writer with strategy-neutral mutation input
| Field | Type | Notes |
|------|------|-------|
| `change_type` | string | Existing finding change type or equivalent classification |
| `severity` | string | Existing severity family |
| `fingerprint_basis` | array<string, mixed> | Stable values used by finding recurrence logic |
| `evidence_payload` | array<string, mixed> | Strategy-owned evidence detail |
| `auto_close_eligible` | boolean | Whether absence in the current run should close the finding |
## Relationships
- One `CompareStrategyCapability` belongs to one `CompareStrategyKey`.
- One `CompareStrategySelection` is produced for each compare start attempt.
- One `CompareOrchestrationContext` contains exactly one supported `CompareStrategySelection` when a compare run is allowed to start.
- One strategy processes many `CompareSubjectIdentity` values and emits many `CompareSubjectResult` values for one `CompareOrchestrationContext`.
- One `CompareSubjectResult` may yield zero or one `CompareFindingCandidate`.
- Existing finding and summary writers consume `CompareSubjectResult` and `CompareFindingCandidate` without needing to inspect strategy internals directly.
## Validation Rules
### Strategy selection
1. Canonical scope must already be normalized to Baseline Scope V2.
2. Exactly one active strategy family must support all included scope entries.
3. Inactive or unsupported subject types fail selection before compare is enqueued.
4. Mixed strategy families fail selection before compare is enqueued.
5. No implicit fallback to `intune_policy` is allowed.
### Compare-subject result contract
1. Every processed subject must return one `CompareSubjectResult`.
2. Every result must include identity, projection, availability, compare state, trust, evidence quality, and diagnostics.
3. `finding_candidate` may be omitted only when the compare state does not warrant finding persistence.
4. The platform must not need raw strategy-private fields to build summaries, findings, or run diagnostics.
## Transition Rules
### Compare start to strategy selection
1. Receive canonical scope from the baseline profile plus any compare narrowing already allowed by the current workflow.
2. Resolve the compatible strategy family from the registry.
3. Reject unsupported or mixed scope before creating subject work.
4. Persist the selected strategy family and compatibility diagnostics into existing compare run context only when a run is created.
### Strategy selection to subject processing
1. Build `CompareOrchestrationContext` from existing baseline, tenant, snapshot, and run truth.
2. Hand domain-specific subject discovery and classification work to the selected strategy.
3. Reuse generic current-state and hashing helpers where they remain strategy-neutral.
### Subject results to existing run and finding truth
1. Aggregate `CompareSubjectResult` values into existing compare summary counts and trust semantics.
2. Write or update findings through the existing unified finding lifecycle using `CompareFindingCandidate`.
3. Persist strategy diagnostics only as secondary run or evidence detail inside existing persistence shapes.
4. Preserve existing run outcome ownership and current operator-visible compare semantics.