Files
git.stella-ops.org/docs/modules/findings-ledger/prep/2025-11-20-ledger-risk-66-prep.md
master d519782a8f
Some checks failed
Docs CI / lint-and-preview (push) Has been cancelled
prep docs and service updates
2025-11-21 06:56:36 +00:00

3.6 KiB
Raw Blame History

Ledger Risk Schema Prep — PREP-LEDGER-RISK-66-001/002

Status: Prep complete (2025-11-20) Owners: Findings Ledger Guild · Risk Engine Guild Scope: Contract + data model for PREP-LEDGER-RISK-66-001/002 (risk scoring fields and deterministic upsert).

Field definitions (canonical finding projection)

  • risk_score (numeric, 0100, 2dp) — monotonic per (finding_id, profile_version); computed by Risk Engine.
  • risk_severity (enum) — derived mapping: critical >= 90, high >= 70, medium >= 40, low >= 10, informational < 10.
  • risk_profile_version (string) — semantic version of the scoring policy/profile; required.
  • risk_explanation_id (uuid/string) — pointer to Risk Engine explanation payload stored in Risk service (not duplicated in ledger).
  • risk_event_sequence (long) — ledger sequence of the last applied risk event; enforces monotonic updates.
  • risk_updated_at (ISO-8601 UTC) — when the score was last written.

Storage and indexes (MongoDB)

  • Collection: findings (existing). Add fields above to the projection document.
  • Unique compound index: { tenant: 1, finding_id: 1, risk_profile_version: 1 }.
  • Query helper index for exports/UI: { tenant: 1, risk_severity: 1, risk_score: -1, observed_at: -1 }.
  • TTL: none; scores are historical but superseded by deterministic upsert described below.

Deterministic upsert flow (LEDGER-RISK-66-002)

  1. Risk Engine emits RiskScoreApplied event with {tenant, finding_id, profile_version, score, explanation_id, event_sequence}.
  2. Handler loads current projection by (tenant, finding_id); compares (profile_version, event_sequence):
    • If incoming event_sequence < stored risk_event_sequence → ignore (idempotent).
    • If equal → idempotent update allowed only when score/severity unchanged.
    • If greater → write new values and set risk_event_sequence = event_sequence.
  3. All writes recorded in ledger append with same event_sequence for audit; projection updates deterministic by sequence ordering.
  4. Exports (/ledger/export/findings) surface these fields; snapshot bundles reuse the same shape.

API/SDK contract hooks

  • OAS baseline will mark all four fields in the finding shapes (canonical + compact) as optional today, required once migrations finish.
  • /ledger/export/findings filters: risk_profile_version (already reserved), add risk_severity and risk_score_min/max in the next OAS bump.
  • UI/SDK must treat missing risk_profile_version as “not yet scored”.

Migration/rollout plan (LEDGER-RISK-66-001)

  • Step 1: Add fields and indexes behind feature flag RiskScoringEnabled (default off).
  • Step 2: Backfill for latest profile per tenant using Risk Engine batch export; write via deterministic upsert to enforce ordering.
  • Step 3: Enable streaming ingestion of RiskScoreApplied events; monitor lag via metric ledger_risk_score_apply_lag_seconds.
  • Step 4: Flip default for RiskScoringEnabled to on after backfill success criteria:
    • 99.9% of existing findings have risk_profile_version populated.
    • No rejected events due to sequence regressions in the last 24h.
  • Step 5: Update OAS/SDK to mark fields required; notify UI/Export consumers.

Observability

  • Log: ledger.risk.apply with tenant, finding_id, profile_version, score, event_sequence, applied (bool).
  • Metrics: ledger_risk_apply_total{result}; ledger_risk_score_latest{severity} gauges per tenant.
  • Tracing: span ledger.risk.apply tagging profile_version, event_sequence, idempotent.

Handoff

  • This document is the prep artefact for PREP-LEDGER-RISK-66-001/002. Implementation tasks wire schema + deterministic upsert and extend exports/OAS accordingly.