TenantAtlas/specs/255-enforce-finding-creation-invariants/data-model.md
ahmido 51ea80ca05
Some checks failed
PR Fast Feedback / fast-feedback (pull_request) Failing after 1m5s
Automatische PR: 255-enforce-finding-creation-invariants → platform-dev (#298)
Automatisch erstellt: Commit & Push aus Workspace (WIP)

Co-authored-by: Ahmed Darrazi <ahmed.darrazi@live.de>
Reviewed-on: #298
2026-04-29 12:26:21 +00:00

4.9 KiB

Data Model — Enforce Creation-Time Finding Invariants

Spec: 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.