Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m5s
Automatisch erstellt: Commit & Push aus Workspace (WIP) Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de> Reviewed-on: #298
130 lines
4.9 KiB
Markdown
130 lines
4.9 KiB
Markdown
# Data Model — Enforce Creation-Time Finding Invariants
|
|
|
|
**Spec**: [spec.md](spec.md)
|
|
|
|
This feature introduces no new persisted truth. The data-model impact is to make the existing `Finding` lifecycle contract explicit at create, refresh, and reopen time across the three active writer families.
|
|
|
|
## Existing Canonical Entities Reused
|
|
|
|
### Finding (`findings`)
|
|
|
|
**Purpose**: Tenant-owned findings workflow truth.
|
|
|
|
**Key fields already in use**:
|
|
- `id`
|
|
- `workspace_id`
|
|
- `tenant_id`
|
|
- `finding_type`
|
|
- `source`
|
|
- `scope_key`
|
|
- `fingerprint`
|
|
- `recurrence_key`
|
|
- `severity`
|
|
- `status`
|
|
- `first_seen_at`
|
|
- `last_seen_at`
|
|
- `times_seen`
|
|
- `sla_days`
|
|
- `due_at`
|
|
- `reopened_at`
|
|
- `resolved_at`
|
|
- `resolved_reason`
|
|
- `closed_at`
|
|
- `closed_reason`
|
|
- `current_operation_run_id`
|
|
- `baseline_operation_run_id`
|
|
|
|
**Feature use**:
|
|
- Remains the single persisted source of truth for active findings lifecycle state.
|
|
- Continues to require both `workspace_id` and `tenant_id` anchors.
|
|
- Keeps the current status families unchanged.
|
|
- Carries the lifecycle-ready fields that this feature hardens at write time.
|
|
|
|
### OperationRun (`operation_runs`)
|
|
|
|
**Purpose**: Existing execution context for baseline compare and other operational flows.
|
|
|
|
**Feature use**:
|
|
- Remains contextual only.
|
|
- `current_operation_run_id` continues to identify the current writer run where the family already sets it.
|
|
- No new operation type or new run-tracking artifact is introduced.
|
|
|
|
### StoredReport (`stored_reports`)
|
|
|
|
**Purpose**: Existing stored reporting artifact for permission posture output.
|
|
|
|
**Feature use**:
|
|
- Unchanged.
|
|
- Mentioned only because permission posture finding generation already correlates lifecycle-ready findings with an existing report artifact.
|
|
|
|
## Derived Non-Persisted Contracts
|
|
|
|
### LifecycleReadyFinding (derived contract)
|
|
|
|
**Definition**: A `Finding` record that is immediately usable by the existing workflow the moment the active writer persists or refreshes it.
|
|
|
|
**Required fields**:
|
|
- active canonical status on first create (`new`)
|
|
- `first_seen_at`
|
|
- `last_seen_at`
|
|
- `times_seen >= 1`
|
|
- `sla_days` when the current severity policy returns a value
|
|
- `due_at` when the current severity policy requires due-date truth
|
|
- existing run correlation fields preserved where the writer already populates them
|
|
|
|
**Removal rule**:
|
|
- no later repair surface may be required for these fields on active writers
|
|
|
|
### RecurrenceIdentity (derived contract)
|
|
|
|
**Definition**: The family-owned identity that decides whether a repeated observation refreshes one canonical finding or incorrectly creates a duplicate.
|
|
|
|
**Family-specific variants**:
|
|
- baseline compare: `recurrence_key` and `fingerprint` derived from tenant, baseline profile, policy type, subject key, and change type
|
|
- Entra admin roles: existing role-assignment and aggregate fingerprints
|
|
- permission posture: existing permission and error fingerprints
|
|
|
|
**Guarantee**:
|
|
- repeated observation of the same canonical issue reuses one finding identity
|
|
|
|
### ObservationBoundary (derived contract)
|
|
|
|
**Definition**: The family-specific rule that decides whether `times_seen` should advance.
|
|
|
|
**Family-specific variants**:
|
|
- baseline compare: same `current_operation_run_id` must not increment `times_seen` twice for the same observation
|
|
- Entra admin roles: later `observedAt` advances seen history
|
|
- permission posture: later `observedAt` advances seen history
|
|
|
|
**Guarantee**:
|
|
- retries and repeated processing do not double count the same observation
|
|
|
|
## State Transitions Reused
|
|
|
|
### Create
|
|
|
|
- missing canonical finding identity -> create one `Finding`
|
|
- resulting state remains `new`
|
|
- lifecycle-ready fields are populated in the same write path
|
|
|
|
### Refresh Existing Open Finding
|
|
|
|
- existing open finding remains in its current active workflow state
|
|
- evidence or severity may refresh according to the writer family
|
|
- missing lifecycle-ready fields covered by this feature are repaired inline
|
|
- valid existing lifecycle fields should not be needlessly reset
|
|
|
|
### Reopen Existing Terminal Finding
|
|
|
|
- existing terminal finding transitions through `FindingWorkflowService::reopenBySystem()`
|
|
- resulting state becomes `reopened`
|
|
- `resolved_*` and `closed_*` markers clear according to the current service behavior
|
|
- SLA and due-state truth are recalculated from the later re-observation moment
|
|
|
|
## Invariant Rules
|
|
|
|
- No new persisted entity, table, or compatibility artifact may be introduced.
|
|
- No new workflow status, reopen reason family, or lifecycle label may be introduced.
|
|
- Active writers must repair incomplete lifecycle-ready fields inline rather than relying on CLI repair commands, tenant maintenance actions, or deploy-time hooks.
|
|
- Due-state repair should fill missing truth or refresh terminal-to-reopened truth only; it must not silently redesign current due-date semantics for already-healthy open findings.
|
|
- A later database constraint is a separate follow-up candidate only if application-level write-path enforcement proves insufficient. |