# Data Model: Baseline Capture Truthful Outcomes and Upstream Guardrails

## Overview

This feature does not add a new persisted entity. It tightens the behavioral contract of three existing truths:

1. `BaselineProfile.active_snapshot_id` defines the current consumable baseline anchor.
2. `BaselineSnapshot.lifecycle_state` plus `completion_meta_jsonb` define whether a captured artifact is consumable.
3. `OperationRun` outcome, summary counts, and context define the operator-visible truth for blocked and no-data capture attempts.

## Entities

### BaselineProfile

**Table / Model**: `baseline_profiles` / `App\Models\BaselineProfile`

**Relevant fields**:

| Field | Type | Purpose in this feature |
|------|------|--------------------------|
| `id` | integer | Baseline identity |
| `workspace_id` | integer | Workspace isolation boundary |
| `status` | enum | Only active profiles can promote a new current snapshot |
| `capture_mode` | enum | Existing capture fidelity setting |
| `scope_jsonb` | jsonb | Determines in-scope policy types / foundations |
| `active_snapshot_id` | nullable integer | Current baseline truth pointer |

**Relationships**:

- `activeSnapshot(): BelongsTo`
- `snapshots(): HasMany`
- `resolveCurrentConsumableSnapshot(): ?BaselineSnapshot`

**Behavioral rule added by this feature**:

- `active_snapshot_id` only changes when the capture attempt yields a consumable snapshot.
- Blocked latest-inventory preconditions and zero-subject/no-data captures must not clear or advance the pointer.

### BaselineSnapshot

**Table / Model**: `baseline_snapshots` / `App\Models\BaselineSnapshot`

**Relevant fields**:

| Field | Type | Purpose in this feature |
|------|------|--------------------------|
| `id` | integer | Snapshot artifact identity |
| `workspace_id` | integer | Workspace isolation boundary |
| `baseline_profile_id` | integer | Owning baseline profile |
| `snapshot_identity_hash` | string | Deduplication / equality proof for captured content |
| `captured_at` | timestamp | Artifact recency |
| `completed_at` | nullable timestamp | Completion boundary |
| `lifecycle_state` | enum | `building`, `complete`, `incomplete` |
| `summary_jsonb` | jsonb | Aggregate capture counts / fidelity / gaps |
| `completion_meta_jsonb` | jsonb | Completion proof and finalization reason details |

**Existing lifecycle reused**:

- `complete`: Consumable snapshot truth.
- `incomplete`: Non-consumable artifact truth.

**Behavioral rule added by this feature**:

- If a zero-subject capture persists a snapshot row, it remains `incomplete` and non-consumable.
- The no-data finalization reason is stored in `completion_meta_jsonb` rather than introducing a new lifecycle state.
- Zero-subject capture must not reuse a historical complete snapshot as if it were the result of the current attempt.

**Expected completion metadata keys**:

| Key | Type | Meaning |
|-----|------|---------|
| `expected_items` | integer | Number of items the job expected to persist |
| `persisted_items` | integer | Number of items actually persisted |
| `producer_run_id` | integer | Owning `baseline_capture` run |
| `was_empty_capture` | boolean | Indicates zero-subject/no-data attempt |
| `finalization_reason_code` | string | Existing or new baseline reason code when incomplete |

### OperationRun

**Table / Model**: `operation_runs` / existing Operations subsystem

**Relevant fields**:

| Field | Type | Purpose in this feature |
|------|------|--------------------------|
| `id` | integer | Operation identity |
| `operation_type` | enum/string | `baseline_capture` |
| `status` | enum/string | `queued`, `running`, `completed` |
| `outcome` | enum/string | `blocked`, `partially_succeeded`, `succeeded`, existing failure outcomes |
| `summary_counts` | json | Flat numeric counts only |
| `context` | json | Detailed capture explanation |

**New or newly-required context keys**:

| Path | Type | Meaning |
|------|------|---------|
| `baseline_capture.reason_code` | string | Dominant blocked or no-data reason |
| `baseline_capture.inventory_sync_run_id` | nullable integer | Latest relevant inventory basis consulted |
| `baseline_capture.subjects_total` | integer | Number of in-scope subjects discovered when subject evaluation runs |
| `baseline_capture.current_baseline_changed` | boolean | Whether the capture attempt changed current consumable truth |
| `baseline_capture.eligibility` | object/array | Optional structured detail about upstream inventory credibility |
| `result.snapshot_id` | nullable integer | Persisted snapshot artifact, if any |
| `result.snapshot_lifecycle` | nullable string | Lifecycle of the persisted or reused snapshot artifact when one is attached to the result |

**Outcome rules introduced or tightened by this feature**:

- `completed + blocked`: The run started, but the latest inventory basis was not credible when execution actually occurred.
- `completed + partially_succeeded`: Zero-subject/no-data capture or existing warning/gap semantics where a run completed without producing a full trustworthy baseline refresh.
- `completed + succeeded`: Reserved for captures that produce or reuse a consumable snapshot truth and leave the effective baseline anchored to that consumable snapshot.

### Upstream Inventory Basis

**Source**: Existing inventory sync `OperationRun` and `InventoryItem` records

**Purpose in this feature**:

- Determine whether a baseline capture may start.
- Determine whether a queued capture is still valid when it executes.
- Determine how many in-scope subjects are currently available for capture.

**Behavioral rule added by this feature**:

- Only the latest relevant inventory sync may authorize capture.
- No earlier successful run may be used as silent fallback when a newer relevant run is blocked, failed, or missing.

## Relationships

| From | To | Relationship | Feature consequence |
|------|----|--------------|---------------------|
| `BaselineProfile` | `BaselineSnapshot` | one-to-many | A profile may have multiple attempted snapshots, but only consumable ones may become current truth |
| `BaselineProfile` | `BaselineSnapshot` | one active snapshot pointer | Pointer remains on last consumable snapshot when new attempt is blocked or no-data |
| `OperationRun` | `BaselineProfile` | contextual | Capture run context references the profile being captured |
| `OperationRun` | `BaselineSnapshot` | contextual | Run context references produced artifact if one exists |
| `InventoryItem` / inventory sync run | `BaselineProfile` capture attempt | derived eligibility | Determines whether capture may produce trustworthy baseline truth |

## State Transitions

### Capture start-time preflight

| Condition | Run created? | Result |
|-----------|--------------|--------|
| Profile inactive / archived / tenant mismatch / scope empty | no | Existing precondition rejection |
| Latest relevant inventory basis missing / blocked / failed / unusable | no | Shared baseline-capture reason code returned to start surface |
| Latest relevant inventory basis credible | yes | `baseline_capture` run enqueued |

### Queued runtime execution

| Condition | Run terminal state | Snapshot effect | Current baseline effect |
|-----------|--------------------|-----------------|-------------------------|
| Latest relevant inventory becomes non-credible after enqueue | `completed + blocked` | none | unchanged |
| Credible inventory but `subjects_total = 0` | `completed + partially_succeeded` | optional non-consumable no-data artifact | unchanged |
| Credible inventory and consumable capture produced or reused | `completed + succeeded` or existing warning-driven `partially_succeeded` | consumable snapshot | remains anchored to the consumable current snapshot |
| Persist / completion proof failure | existing failure / incomplete semantics | incomplete snapshot | unchanged |

## Invariants

- A non-consumable snapshot must never become current baseline truth automatically.
- A green baseline-capture outcome must imply that a consumable snapshot truth exists after the run.
- `summary_counts` stay flat and numeric-only even when blocked/no-data truth is carried by context and reason code.
- Compare-readiness surfaces derive from consumable baseline truth, not merely from the existence of a latest run.

## Conditional Legacy Edge

Existing legacy backfill logic can classify historical empty captures as `complete`. This feature does not change historical rows by default, but if implementation proves that those historical rows still participate in current runtime truth, the legacy classification rule must be adjusted inside this feature and re-proved with `BaselineSnapshotBackfillTest`.