feat(deploy): TrueNAS Custom-App + Auto-Deploy, plus aufgelaufene Arbeit
Some checks failed
CI / Backend Tests (.NET) (push) Successful in 1m11s
CI / Frontend Tests (Node/Vite) (push) Failing after 4m59s
CI / Docker Build & Push (push) Has been skipped
CI / Deploy auf TrueNAS (Custom App) (push) Has been skipped

Deployment:
- custom-app.compose.yaml: self-contained Compose fuer TrueNAS "Custom App"
  (absolute Host-Bind-Pfade, postgres:18, pull_policy always, Port 8090)
- scripts/truenas-deploy.sh: Host-Skript create/redeploy via midclt (App
  bleibt unter Apps sichtbar) inkl. Image-Pull + Health-Check
- ci.yml Deploy-Job: laeuft auf ubuntu-latest-Runner, kopiert Deploy-Dateien
  per SSH auf den NAS-Host und triggert truenas-deploy.sh (statt runs-on goldeye)
- compose.yaml/.env.example: postgres:18 (Locale-Match zur Quell-DB), Port 8090
- .gitignore: .agents/, tools/rag/, deploy/truenas/.env (Secrets/Scratch)

Aufgelaufene Feature-Arbeit (verified/Freeze, Migrationen, Import-Triage):
- GerbilOverride/VerifiedGerbil-Endpoints + GerbilSnapshotService + Tests
- EF-Migrationen (ShowInChronicle, Stillborn, BirthOrder, ManualFlag, DSGVO)
- Frontend VerifizierteTierePage + verified-API + e2e-Spec
- diverse Import-/Triage-Skripte und -Tests

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-07-19 09:19:11 +02:00
parent 84365bba7f
commit 45b8533f18
93 changed files with 20477 additions and 432 deletions

View File

@@ -0,0 +1,62 @@
using System.ComponentModel.DataAnnotations;
namespace GerbilManagerWebAPI.Models
{
/// <summary>
/// VERIFIED/PROTECTED OVERRIDE: authoritative, hand-curated per-gerbil field values. Decoupled
/// from the rest of the model on purpose — <see cref="GerbilId"/> is a plain Guid column (NOT an
/// enforced foreign key, no navigation property), so the import re-ingest wipe
/// (<c>IngestResolvedService</c>) can delete and reload gerbils without deleting or breaking
/// override rows. After every ingest the override is re-applied on top of the fresh import (the
/// "freeze"), so the breeder's curated values are never lost. Matched to the (deterministic)
/// gerbil id. Same ingest-surviving pattern as <see cref="Feedback"/>.
///
/// Two flavours, both stored here:
/// - <see cref="IsVerified"/> = true → "vollständig korrekt": the whole own-field freeze set is
/// pinned, and a full Akte snapshot (<see cref="SnapshotJson"/>) is kept for the drift report,
/// export and regression test.
/// - <see cref="IsVerified"/> = false → "geschützt": created by a manual edit; only the changed
/// fields are pinned (per-field) so untouched fields keep receiving import improvements.
/// </summary>
public class GerbilOverride
{
[Key]
public Guid Id { get; set; }
/// <summary>Loose reference (no FK) to the gerbil this override curates.</summary>
public Guid GerbilId { get; set; }
/// <summary>Captured gerbil name — keeps the row human-readable and survives an ingest wipe.</summary>
public string? EntityName { get; set; }
/// <summary>true = "vollständig korrekt" (full freeze + certified); false = "geschützt" (per-field edit).</summary>
public bool IsVerified { get; set; }
/// <summary>Frozen own-field values as a JSON object (field name → value). Verified rows carry
/// the full freeze set; protected rows carry only the manually-changed fields. Re-applied on
/// top of the fresh import after every ingest.</summary>
public required string OverrideJson { get; set; }
/// <summary>Full human-readable Akte snapshot (self + parents + own litters/children) captured
/// at verify time — feeds the drift report, export and regression test. null for protected-only rows.</summary>
public string? SnapshotJson { get; set; }
/// <summary>Raw-import Akte snapshot captured during the LAST ingest, BEFORE the freeze was
/// applied — lets the UI show where the raw import disagrees with the golden state. null until
/// the first ingest after verifying.</summary>
public string? LastImportSnapshotJson { get; set; }
/// <summary>Cached diff (golden ↔ last raw import) as a JSON array, computed at ingest time.
/// null/empty = no drift / not yet computed.</summary>
public string? LastImportDiffJson { get; set; }
/// <summary>Optional free-text note the breeder attached when verifying.</summary>
public string? Note { get; set; }
/// <summary>When the row was marked "vollständig korrekt"; null for protected-only rows.</summary>
public DateTimeOffset? VerifiedAt { get; set; }
/// <summary>When the override was last created/updated.</summary>
public DateTimeOffset UpdatedAt { get; set; }
}
}