Files
nexarch/docs/CUT-01-PRUEFPROTOKOLL.md
patrickandClaude Sonnet 5 7f37126049
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (push) Canceled after 0s
CI / Web-Barrierefreiheitspruefung (axe, Tastatur, Reflow, Textabstaende) (pull_request) Canceled after 0s
Core-Schnittstellen-Vertragstests / contract-tests (pull_request) Canceled after 0s
Archive-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (push) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (push) Canceled after 0s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (push) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (push) Canceled after 0s
CI / Web (dms-browser) (push) Canceled after 0s
CI / Web (pdf-viewer) (push) Canceled after 0s
CI / Web (shl) (push) Canceled after 0s
CI / Web (dms-browser) (pull_request) Canceled after 0s
CI / Core-Modul (Build/Vet/Test) (pull_request) Canceled after 0s
CI / DMS Security-Test-Gate (AC5, Merge-Gate) (pull_request) Canceled after 0s
CI / Connect Security-Test-Gate (QA-01 AC4, Merge-Gate) (pull_request) Canceled after 0s
CI / DMS-Modul Supply-Chain-Scan (govulncheck) (pull_request) Canceled after 0s
CI / Web (pdf-viewer) (pull_request) Canceled after 0s
CI / Web (shl) (pull_request) Canceled after 0s
Supply-Chain-Scan (Go) / govulncheck (pull_request) Canceled after 0s
Mail-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
Supply-Chain-Scan (npm) / npm-audit (pull_request) Canceled after 0s
Core-Pflichttest-Gate / pflichttest-gate (pull_request) Canceled after 0s
RBAC-Rollenhierarchie-Pflichtgate / rbac-policy-invariant (pull_request) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (push) Canceled after 0s
CI / DMS-Modul (Lint/Build/Test/Coverage/Artefakt) (pull_request) Canceled after 0s
CUT-01: Dokubegruendung DOK-03-Abhaengigkeit korrigieren (COR-02 statt COR-03)
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-14 23:15:51 +02:00

23 KiB
Raw Permalink Blame History

CUT-01 – Prüfprotokoll: Trockenlauf 1 – Entwicklung

Branch feature/cut-01-trockenlauf-1-entwicklung, Testhost root@192.168.1.204, Wegwerf-Cluster coredev (PostgreSQL 17, Port 5433, Zugang über TEST_ADMIN_DSN aus /etc/nexarch-dev/core-dev.env, nie ausgegeben). Keine Verbindung zu archivmail/archivdms oder deren Daten, kein Zugriff auf den Cluster main (5432) und die dort laufende archivdms-Instanz.

Umfang

Zentrales Orchestrierungswerkzeug für den ersten Trockenlauf aller Migrationsskripte. Es erzeugt eine vollständig synthetische Testkopie beider Altsysteme, führt die bestehenden Migratoren in technisch korrekter Reihenfolge aus und protokolliert jeden Schritt (JSON + Text). Migrationslogik wird nicht dupliziert; das Werkzeug liefert Reihenfolge, Quell-Adapter gegen die Testkopie, Testkopie und Protokoll.

Pfad Inhalt
cmd/cutover-dryrun Einstiegspunkt (Flags: -repo, -arbeitsverzeichnis, -seed, -bcrypt-kosten, -bezeichnung, -behalten)
internal/cutoverorchestrator Ablaufplan + Abhängigkeitsprüfung (plan.go), Runner mit Abbruch/Protokoll (runner.go, protocol.go), Redigierung (redact.go), Generator der Testkopie (dataset.go, legacyschema.go, seed.go), Anonymisierungsnachweis (anonymization.go), Export-Sichten (export.go), Schritte (steps_*.go), Modulstufen-Aufruf (stages.go), Cluster-Hilfen (cluster.go)
dms/cmd/cutover-stage, dms/internal/cutoverstage DMS-Stufe: Schema, DOK-01, DOK-02, DOK-04, DOK-05, VAL-02-Entschlüsselung
mail/cmd/cutover-stage, mail/internal/cutoverstage Mail-Stufe: MAI-02
archive/cmd/cutover-stage, archive/internal/cutoverstage Archive-Stufe: Schema, TAR-01, MAI-03, MAI-04, TAR-02
workflow/cmd/cutover-stage, workflow/internal/cutoverstage Workflow-Stufe: Schema, WFL-01
docs/cut-01/iteration-1, docs/cut-01/iteration-2 Original-Protokolle aller Läufe (Text, JSON, VAL-01-/VAL-02-Berichte)

Warum Modulstufen als eigene Prozesse: dms/, mail/, archive/ und workflow/ sind eigene Go-Module; ihre internal-Pakete (die Migratoren) sind aus dem Root-Modul nicht importierbar. Der Orchestrator baut je Modul ein Stufen-Binary (go build), startet es mit minimaler Umgebung (Zugangsdaten nur per Umgebungsvariable, nie als Argument) und liest das Ergebnis als JSON. Gemeinsamer Laufkontext: kontext.json (DB-Namen, Tenant-Zuordnung, keine Zugangsdaten). Übergaben zwischen Modulen (z. B. DOK-01 → TAR-01: Ziel-IDs der Dokumente) laufen über Dateien unter uebergabe/<slug>/.

Datenbank-Topologie im Lauf (alle Namen mit Laufkennung cut01xxxxxx, dadurch keine Kollision mit fremden Test-DBs, Aufräumen nur über diese Kennung):

Datenbank Inhalt
<lauf>_quelle_archivmail, <lauf>_quelle_archivdms synthetische Testkopie der Altsysteme
<lauf>_registry Core-Registry (alle migrations/*.up.sql) inkl. Maps, object_acl, audit_events
tenant_<slug> (von COR-01 provisioniert) Core-Tenant-Schema + DMS-Schema (gleiche physische DB, siehe DMS FDN-02)
<slug>_archive Archive-Schema je Mandant (Modell C)
<slug>_workflow Workflow-Schema je Mandant (eigene DB: module_registrations existiert in Archive und Workflow)

Ablaufplan und Reihenfolge

Der Plan wird vor dem ersten Datenbankzugriff von ValidatePlan geprüft: jede Abhängigkeit muss im Plan vorher stehen, sonst läuft kein einziger Schritt.

# Schritt Paket / Funktion hängt ab von Begründung
1 PREP-01 Arbeitsverzeichnis, Modulstufen bauen –
2 PREP-02 Testkopie erzeugen PREP-01
3 PREP-03 Anonymisierungsnachweis PREP-02 vor jeder Verarbeitung
4 PREP-04 Core-Registry (Core-Migrationen) PREP-01 Maps/Registry-Tabellen
5 EXP-01 Export-Sichten (Iteration-1-Korrektur I-1) PREP-03 Exportform für MAP-01/MAP-03
6 MAP-01 tenantmatch.ExportTenants/Match EXP-01 Tenant-Identität vor allem anderen
7 MAP-02 tenantapproval.InsertCandidates/Approve/Correct MAP-01, PREP-04 Freigabe Pflicht vor COR-01
8 MAP-03 usermatch.ExportUsers/Match MAP-02 Abgleich nur in freigegebenen Tenants
9 MAP-04* Trockenlauf-Ersatz: user_migration_map/role_migration_map MAP-03 MAP-04 ist nicht gebaut (siehe Befunde)
10 COR-01 migrationtenant + tenant.Provisioner MAP-02 legt tenant_<slug> an
11 SCHEMA-01 migrate.Orchestrator.RolloutAll (TEN-07) COR-01 users-Tabelle vor DMS-Schema
12 SCHEMA-02 DMS migrate.Up, Archive-SQL, Workflow migrate.Up SCHEMA-01 DMS braucht users
13 COR-02 migrationuser (inkl. LDAP-Felder COR-04) MAP-04*, SCHEMA-01
14 COR-04 Prüfung user.LDAPInfo gegen Map COR-02 COR-04 hat keinen eigenen Migrator, steckt in COR-02
15 COR-03 migrationrole COR-02 role_assignments → FK users
16 COR-05 Staging aus audit_log + migrationaudit MAP-04*, COR-01 braucht beide Maps
17 DOK-01 migrationdocument COR-02, SCHEMA-02 created_by → migrierte Benutzer
18 DOK-02 migrationfolder DOK-01 verknüpft migrierte Dokumente
19 DOK-04 migrationmetadata DOK-01 Feldwerte an Dokumenten
20 DOK-03 migrationacl + Core-Gruppen (rbac.GroupStore) DOK-01, COR-02 Objektrechte auf migrierte Dokumente/Gruppen (korrigiert durch Verifikation: tatsächliche Abhängigkeit ist COR-02/user_migration_map, nicht COR-03; Ausführungsreihenfolge war davon unberührt, da COR-02 ohnehin vor COR-03 läuft)
21 DOK-05 migrationsharelink DOK-01 FK share_links.document_id
22 MAI-02 messagemigration COR-01 Ziel-Slug
23 TAR-01 retentionmigration DOK-01, MAI-02, SCHEMA-02 ObjectReference kommt aus DOK-01/MAI-02
24 MAI-03 dsgvorequestmigration TAR-01 sucht Objekte der betroffenen Person
25 MAI-04 reconcilereportmigration SCHEMA-02
26 TAR-02 deletionrequestmigration TAR-01, COR-02 gleiche Retention-Objekte, Antragsteller → NEXARCH-ID
27 WFL-01 migrationworkflow COR-02, SCHEMA-02 Akteursauflösung
28 VAL-01 migrationverify.Run COR-02, DOK-01, MAI-02
29 VAL-02 migrationverify.CompareSamples DOK-01, MAI-02
30 VAL-05 auth.LoginService.Login COR-02
31 CHK-01 modulübergreifende Kettenprüfung DOK-03, DOK-05, TAR-02, MAI-03, MAI-04, WFL-01

Abbruchverhalten: beim ersten Fehler wird abgebrochen, alle Folgeschritte werden mit „nicht ausgeführt: Abbruch nach Fehler in X“ protokolliert (die Einzelpakete arbeiten selbst fail-closed, ein Weiterlaufen würde nur Folgefehler protokollieren). Panics werden als Fehler des Schritts erfasst.

Synthetische Testkopie

Erzeugt ausschließlich von GenerateDataset (deterministisch über -seed, Standard 20261014). Strukturell nach datenmodell-altsysteme.md: archivmail mit tenants/tenant_domains, users.username/role/source, emails + storage_objects + attachments + email_attachments + email_refs, dsgvo_requests, reconciliation_reports, append-only audit_log; archivdms mit users inkl. auth_source/ldap_uid/ldap_synced_at, permission_groups/_members, document_visibility, document_grants (inkl. deny), akten, WORM-documents ohne Revisionstabelle, custom_field_defs, classification_templates, document_shares, document_delete_requests, workflows/workflow_runs, audit_log.

Abgebildete Konstellationen: Mandant in beiden Systemen per Slug-Treffer („Kanzlei Musterhausen“), Mandant nur über die manuelle Kundenzuordnungsliste zuordenbar („Steuerbüro Beispielstadt“, abweichende Slugs, zwei Domains), je ein Mandant nur in archivmail („Praxis Fantasia“) bzw. nur in archivdms („Ingenieurbüro Erfundenhausen“); Doppelkonten mit unterschiedlicher Groß-/Kleinschreibung; LDAP-Konten beider Feldtiefen; alle dokumentierten archivmail-Rollen außer superadmin; Fristen in Zukunft/Vergangenheit/keine; aktive, abgelaufene, widerrufene Freigabelinks und Link auf gelöschtes Dokument; offene/vollzogene/abgebrochene Löschanfragen; laufende, abgeschlossene, abgebrochene Workflows; DSGVO-Anfragen open/partial/ completed; eine Mail mit Sichtbarkeit in zwei Mandanten.

Mengen je Lauf: 6 Altsystem-Mandanten, 30 Konten, 24 Dokumente, 18 Mails, 9 Anhänge, 48 Binärobjekte, 431 Tabellenzeilen.

Akzeptanzkriterium 3: Anonymisierung nachweislich wirksam

Argumentation: Die Testkopie ist keine anonymisierte Kopie echter Daten, sondern von Grund auf erzeugt. Das Werkzeug hat keinen Eingang für Fremddaten: GenerateDataset(seed, now, bcryptCost) hat keinen Parameter für Dateien, DSNs oder Exporte, die einzigen Datenquellen sind Konstanten und ein Pseudozufallsgenerator in internal/cutoverorchestrator/dataset.go. Die Quell-Datenbanken werden in jedem Lauf neu angelegt (CREATE DATABASE) und ausschließlich daraus befüllt. Es existiert kein Codepfad, der eine Verbindung zu archivmail/archivdms aufbaut. Echte Personendaten können daher im Testlauf nicht vorkommen.

Technischer Nachweis je Lauf (Schritt PREP-03, VerifyAnonymization), unabhängig vom Generator: jede Zeile jeder Tabelle beider Quell-DBs wird als JSON gelesen und durchsucht.

Prüfung Zielwert Ergebnis (Läufe 2.1 und 2.2)
E-Mail-Adressen nur unter .example (RFC 2606 reserviert) 0 Verstöße 93 Adressen, 0 Verstöße
IP-Adressen nur aus Dokumentationsnetzen (RFC 5737) 0 Verstöße 24 Adressen, 0 Verstöße
Konten mit synthetischem Präfix „testperson“ alle 30/30
Binärobjekte beginnen mit Marker SYNTHETISCH alle 48/48
Tabellen/Zeilen geprüft alle 30 Tabellen, 431 Zeilen

Unit-Tests: TestGenerateDataset_ContainsOnlySyntheticPersonalData (alle erzeugten Personenangaben synthetisch) und TestAnonymizationCheck_DetectsRealLookingData (die Prüfung erkennt eine reale Domain und eine öffentliche IP – der Nachweis ist also wirksam, nicht nur formal). Passwörter: zufällig erzeugt, nur als bcrypt-Hash (Kosten 12) in der Testkopie, Klartext nur im Speicher des Laufs für VAL-05.

Iterationsprotokoll

Iteration 1 – Fehlerfindung (Läufe 1.1 bis 1.5)

Lauf Kennung Abbruch bei Fehler Korrektur vor dem nächsten Lauf
1.1 cut01479c9d MAP-01 tenants-export (archivmail): column "domain" does not exist I-1 siehe unten
1.2 cut01a6173e MAP-02 Vollständigkeit: archivmail/praxis-fantasia, archivdms/ingenieurbuero-erfundenhausen ohne Eintrag in der freigegebenen tenant_migration_map I-2
1.3 cut01c33bb7 COR-03 keine core-rolle fuer archivmail-rolle "auditor" hinterlegt I-3
1.4 cut0171964d MAI-03 offene DSGVO-Anfrage: 2 migrierte Objekte der betroffenen Person, aber keine Einzelentscheidung I-4
1.5 cut01748e71 – alle 31 Schritte erfolgreich –

I-1 – Export-Schemaannahme MAP-01/MAP-03 (Integrationsfehler). tenantmatch.ExportTenants liest tenants(id, slug, name, domain), usermatch.ExportUsers liest users(id, tenant_id, email, name, password_hash). archivmail führt Domains in tenant_domains (1:n), beide Altsysteme kennen nur users.username, archivmail-Superadmins haben tenant_id NULL. Das MAP-01-Prüfprotokoll hatte genau diese Annahme als offen markiert. Bei der Ursachenanalyse zu Lauf 1.1 wurde die gleiche Annahme in MAP-03 per Spaltenabfrage der Testkopie bestätigt und mitbehoben. Korrektur (ohne Änderung der MAP-Pakete): neuer Schritt EXP-01 legt in jeder Quelle das Schema cutover_export mit Sichten tenants (archivmail: erste registrierte Domain je Mandant) und users (username AS name, archivmail nur Konten mit Mandant) an; MAP-01/MAP-03 lesen über Pools mit search_path=cutover_export,public (export.go, Cluster.ConnectSchema).

I-2 – Einseitige Mandanten gingen verloren (Kettenfehler in MAP-02/MAP-03). tenantapproval.InsertCandidates verwarf Kandidaten, die nur in einem Altsystem existieren; usermatch.Match übersprang solche Mandanten. Da die tenant_migration_map die einzige Quelle für COR-01 ist, wären diese Mandanten samt Benutzern, Dokumenten und Mails nie migriert worden – ohne Fehlermeldung. MAP-01 gibt einseitige Kandidaten ausdrücklich aus, „damit sie in der Freigabeprüfung sichtbar bleiben“. Korrektur: InsertCandidates legt einseitige Kandidaten als pending an (nur Kandidaten ganz ohne Quelle werden verworfen), Freigabepflicht unverändert; Match liefert für einseitig freigegebene Mandanten die Benutzer der vorhandenen Seite als einseitige Kandidaten. Test TestInsertCandidates_SkipsOneSidedCandidates ersetzt durch TestInsertCandidates_KeepsOneSidedCandidatesPending (prüft zusätzlich: ohne Freigabe nicht nutzbar), neu TestMatch_EinseitigFreigegebenerTenantLiefertEinseitigeKandidaten.

I-3 – Dokumentierte archivmail-Rollen ohne Core-Rolle (COR-03). archivmailRoleMap kannte domain_admin, auditor, domain_auditor nicht (alle in datenmodell-altsysteme.md belegt) → fail-closed-Abbruch. Korrektur: domain_admin → tenant_admin, auditor/domain_auditor → user (Core hat keine Prüferrolle; geringste Rolle statt Rechteausweitung), superadmin bleibt bewusst ungemappt. Test TestCoreRole_DocumentedArchivmailRoles.

I-4 – DSGVO-Anfragen ohne Objektbezug (Kettenfehler TAR-01 → MAI-03). MAI-03 (dsgvorequest.ProcessDeletionRequest → dpreport.SubjectReport) findet Objekte nur über retention_objects.data_subject_ref; TAR-01 registrierte über retention.RegisterObject ohne diese Referenz. Folge: eine offene Art.-17-Anfrage wurde als „verarbeitet“ gezählt, aber ohne eine einzige Einzelentscheidung. Erkannt durch die Nachprüfung der Archive-Stufe. Korrektur: retentionmigration.SourceObject.DataSubjectRef; gesetzt → retention.RegisterObjectForSubject (bestehende CMP-02-Funktion), sonst unverändert RegisterObject. Die Mail-Stufe liefert den Absender als betroffene Person. Test TestMigrateAll_CarriesDataSubjectRef.

Bereits vor Lauf 1.1 (Integrationsanalyse, keine Laufkorrektur): migrationdocument.DocumentID exportiert die DOK-01-Ziel-ID, weil TAR-01/ TAR-02 im Archive-Modul die ObjectReference brauchen, deriveUUID aber nicht importieren können (gleiches Muster wie messagemigration.MessageID).

Iteration 2 – Bestätigung (Läufe 2.1 und 2.2)

Beide Läufe auf Commit 4fdbb9b (Stand nach allen Iteration-1-Korrekturen), ohne Codeänderung zwischen den Läufen, ohne -behalten (Aufräumen durch das Werkzeug selbst).

Lauf Kennung Ergebnis Dauer aufgeräumt
2.1 cut017f8b14 alle 31 Schritte erfolgreich 28,8 s 15 Lauf-DBs entfernt
2.2 cut01d7d2a2 alle 31 Schritte erfolgreich 27,4 s 15 Lauf-DBs entfernt

Danach: 0 Datenbanken mit Laufkennung auf dem Cluster, alle fremden Test-Datenbanken unverändert.

Prüfungen

# Prüfung Zielwert Ergebnis
T1 Vollständiger Lauf gegen anonymisierte Kopie protokolliert (Ticket-Prüfung 1) Lauf ohne Abbruch, Protokoll vorhanden erfüllt – Läufe 1.5, 2.1, 2.2; Protokolle docs/cut-01/iteration-*/
T2 Mind. zwei Iterationen mit dokumentierten Korrekturen (Ticket-Prüfung 2) ≥ 2 erfüllt – Iteration 1 (5 Läufe, 4 Korrekturen), Iteration 2 (2 Läufe)
AK1 Alle Migrationsskripte laufen mind. einmal vollständig durch jeder Schritt erfolgreich erfüllt – 31/31 in 1.5, 2.1, 2.2 (Ausnahme MAI-01, siehe B-5)
AK2 Gefundene Fehler vor Trockenlauf 2 behoben alle abbrechenden Fehler behoben erfüllt – I-1 bis I-4 behoben und in Iteration 2 bestätigt; nicht abbrechende Befunde mit Fach-/Zielsystem-Entscheidung siehe unten
AK3 Anonymisierung nachweislich wirksam 0 Verstöße, Prüfung wirksam erfüllt – siehe Abschnitt AK3
V1 VAL-01 Zeilenzahl je Tenant/Kerntabelle 0 offene Befunde erfüllt – 12 Vergleiche; Dokumente 24→21, Differenz 3 erklärt (vollzogene Löschungen, nur Audit)
V2 VAL-02 Hash-Stichprobe Binärdateien 0 Abweichungen erfüllt – 15 DMS-Dateien (über crypto.Service entschlüsselt) + 9 Mail-Anhänge, 0 Abweichungen
V3 VAL-05 Login mit migrierten Zugangsdaten alle Stichproben-Logins ok, falsches Passwort abgewiesen erfüllt – 12/12 Logins (3 je Tenant), 12/12 Ablehnungen
V4 TAR-01 Fristen exakt übernommen 0 Abweichungen erfüllt – 40 Objekte geprüft, 27 Fristen identisch, 13 ohne Frist bleiben ohne
V5 COR-05 Audit-Kontinuität Zeitstempel/Aktion unverändert, nichts unmigriert erfüllt – 24/24 Ereignisse
V6 CHK-01 modulübergreifend 0 Befunde erfüllt – je Dokument/Nachricht ein Retention-Objekt (21/19), keine Objektrechte auf nicht migrierte Dokumente, Freigabelinks 6, Workflows 9, offene Löschanfragen 3, vollzogene 3, Akten 9, Reconciliation-Berichte 9 = Quelle
U1 Orchestrator-Tests (Reihenfolge, Fehlerprotokoll, Abbruch) grün erfüllt – go test ./internal/cutoverorchestrator: Reihenfolge, Abbruch nach Fehler mit Protokoll/Konsole/Text, Panic, ungültiger Plan führt nichts aus, Kontext-Abbruch, Redigierung, Abhängigkeiten des echten Plans, Generator, Anonymisierungsprüfung, Slug/DSN
U2 Regression aller orchestrierten Pakete, real gegen Postgres (-p 1) grün erfüllt – 21 Pakete in root/dms/archive/workflow/mail grün, plus dms/internal/cutoverstage
S1 Keine Zugangsdaten in Code/Protokoll keine Treffer erfüllt – DSN nur aus Umgebung, Redigierung (Literal + URL-Userinfo + password=) in Runner und Stufen-Fehlern, Scan der Protokolle ohne Treffer

Offene Befunde ohne Abbruch (Übergabe an CUT-02 und Modul-Boards)

Diese Punkte brechen die Kette nicht ab und verfälschen keine Daten in Richtung Datenverlust oder Rechteausweitung, verlangen aber eine Fach-, Compliance- oder Zielsystem-Entscheidung („Keine Änderung der Zielsystem-Fachlogik selbst“, Ticket CUT-01). Sie sind vor dem Stichtag zu entscheiden.

  • B-1 Archive: abgelaufene Altsystem-Fristen bleiben active. TAR-01 übernimmt retain_until, migrierte Objekte haben aber keine Aufbewahrungsklasse; deletionworkflow.ReleaseExpired wertet nur Klassenzuordnungen aus, nicht retain_until. Eine Mail mit Frist in der Vergangenheit wird von MAI-03 mit „Aufbewahrungsfrist ist noch nicht abgelaufen“ abgelehnt. GoBD-sicher (nichts wird zu früh gelöscht), aber sachlich falsche Begründung gegenüber der betroffenen Person. Empfehlung: RET-02/RET-03 berücksichtigen retain_until (Archive-Board).
  • B-2 MAI-03: abgeschlossene DSGVO-Anfragen hinterlassen keine Spur. logHistorical schreibt nur je noch vorhandenem Objekt; nach einer vollzogenen Löschung gibt es keine Objekte mehr, dsgvo_decision_log verlangt aber ein Objekt. Im Lauf: 3 abgeschlossene Anfragen, 0 Einträge. Entscheidung nötig, wo der historische Nachweis liegt (z. B. Archive-Audit).
  • B-3 Dokumenttypen nicht migriert. archivdms-document_types und documents.doc_type_id haben kein Migrationspaket; DOK-04 belegt document_types mit Klassifikationsvorlagen. Feld-Vorgaben der Vorlagen (classification_template_field_defaults.default_value) gehen verloren, da SourceClassificationTemplate kein Feld dafür hat.
  • B-4 Akten-Status. akten.status (offen/geschlossen) hat im Ordnermodell keine Entsprechung und wird nicht übernommen.
  • B-5 MAI-01 nicht im Lauf. Die Postfach-Migration verlangt je Postfach ein vom Betreiber neu vergebenes Passwort und einen echten IMAP-Test; im Trockenlauf gibt es weder IMAP-Testserver noch sinnvoll erfindbare Zugangsdaten. Gehört in CUT-02 mit Test-IMAP.
  • B-6 Suchindex-Ersatz. MAI-02 schreibt Metadaten nur in Manticore; die einzige Manticore-Instanz auf Host 204 teilt sich das Altsystem archivdms und bleibt tabu. Im Trockenlauf vertritt ein dateibasierter Index (ziel-objekte/mail-index/<slug>.json) den SearchIndexer – gleiches Interface wie die Paket-Tests. CUT-02 braucht eine eigene Manticore-Instanz.
  • B-7 KEK-Ersatz. DOK-01/VAL-02 nutzen statt der Core-KEK-API einen pro Lauf zufälligen Seed (HMAC je Mandant, nur per Umgebungsvariable an die DMS-Stufe). CUT-02 gegen die echte KEK-API.
  • B-8 MAP-04 fehlt. Die menschliche Freigabe der Benutzer-Map ist nicht gebaut; der Trockenlauf gibt jeden MAP-03-Kandidaten mit dem Prüfer „trockenlauf-pruefer (synthetische freigabe, CUT-01)“ frei, übernimmt LDAP-Felder aus der Quelle und füllt role_migration_map (archivdms: erste Berechtigungsgruppe, sonst archivmail-Rolle). Ebenso simuliert MAP-02 die Freigabe und korrigiert per Correct den Ziel-Slug (Bindestrich → Unterstrich, Laufpräfix): Altsystem-Slugs mit Bindestrich verletzen die Core-Slug-Regel, ein Prüfer muss sie bei der Freigabe korrigieren.
  • B-9 archivmail mit mehreren Domains. MAP-01 vergleicht genau eine Domain; die Export-Sicht liefert die erste registrierte.
  • B-10 Plattformkonten. archivmail-Superadmins (tenant_id NULL) und die Rolle superadmin werden bewusst nicht mandantenbezogen migriert.
  • B-11 Mandanteneigene Gruppennamen. COR-03 kennt nur die archivdms-Standardgruppen; eigene Gruppennamen eines Mandanten brechen fail-closed ab. Die Testkopie nutzt nur Standardgruppen; für reale Daten braucht COR-03 eine pflegbare Zuordnung.
  • B-12 Soft-gelöschte Dokumente ohne vollzogene Löschung sind in der Testkopie nicht enthalten; DOK-01 kennt keinen Löschzustand und würde sie als aktiv übernehmen. Der Adapter übernimmt nur deleted_at IS NULL.

Beobachtungen an bestehender Test-Infrastruktur (nicht Teil von CUT-01): Archive-Pakettests teilen sich TEST_TENANT_DSN und kollidieren bei parallelem Paketlauf (mit -p 1 grün); internal/migrationtenant-Test räumt seine Tenant-DBs nicht ab (t.Cleanup nutzt einen per defer bereits geschlossenen Pool) – die durch den eigenen Regressionslauf entstandenen fünf Datenbanken wurden entfernt.

Entscheidungen (QA)

  1. Modulstufen als Prozesse statt go.work/replace: keine Änderung an go.mod der Module, internal-Sichtbarkeit bleibt unangetastet.
  2. Export-Sichten statt Änderung von MAP-01/MAP-03: die Normalisierung gehört zum Export-Dump des Stichtags, die Pakete bleiben schemagenerisch.
  3. Adapter-Entscheidungen (in den Stufen dokumentiert): archivdms-WORM-Datei = Revision 1; boolean→bool, enum→select, monetary→number; widerrufene Links und Links auf gelöschte Dokumente werden nicht angeboten; DSGVO partial/failed = offen; Löschanfrage pending/confirmed/blocked_retention = offen, cancelled entfällt; Antragsteller/Bestätiger werden auf NEXARCH-IDs abgebildet (Vier-Augen- Vergleich); document_visibility liefert Erlaubnisse, document_grants.deny die Verbote.
  4. Datenbankname enthält stets die Laufkennung, Aufräumen nur über sie; das Werkzeug löscht nie fremde Datenbanken.
  5. Abbruch beim ersten Fehler, kein „weiter bei Fehler“-Modus.

Bedienung

set -a && . /etc/nexarch-dev/core-dev.env && set +a   # liefert TEST_ADMIN_DSN, nie ausgeben
go run ./cmd/cutover-dryrun -repo . -arbeitsverzeichnis /root/cut01-dryrun -bezeichnung "Trockenlauf"

Ergebnis: protokoll.json, protokoll.txt, VAL-01-/VAL-02-Berichte im Laufverzeichnis; Exitcode 0 nur bei vollständigem Durchlauf. Mit -behalten bleiben Datenbanken und Arbeitsdateien zur Analyse erhalten.