RAL Gütezeichen Anhörungsportal
Architekturdokumentation (arc42)
12Kapitel
57986Wörter
~289 Min.Lesezeit
arc42 v8.2Template

Stand 27.10.2026 21:52

01. Einführung und Ziele

Aufgabenstellung

Das RAL Gütezeichen Anhörungsportal (CAF.Applications.RAL.QualityMarks.HearingPortal) bildet das Anhörungsverfahren für RAL-Gütezeichen digital ab: Ein Verfahren wird angelegt, Institutionen und deren Kontakte werden als Teilnehmer aufgenommen, Unterlagen werden zusammengestellt und nach Vier-Augen-Freigabe versendet, Teilnehmer geben über die Benutzerkonten ihrer Institution Stellungnahmen ab (RAL legt je Institution dedizierte Konten an, mehrere Benutzerkonten pro Institution — feste Bindung gemäß RAL-QM-HP-31), das Verfahren wird intern beantwortet, beschieden und schließlich abgeschlossen und archiviert. Der Lebenszyklus verzweigt je nach gewählter Abschlussvariante (US-33):

text
Draft → PendingApproval → Approved → Dispatched → Adjudication → Completed        (zweistufig)
Draft → PendingApproval → Approved → Dispatched ─────────────→ Completed          (einstufig)
  └────────────────────── benannte Rücksprünge/Abbruch nach Fachregel ──────────────────────┘

Bei der einstufigen Variante entfällt der eigene Adjudication-Status: Bescheidung und Endfassung gehen in einem Vorgang hinaus. Details zur Wahl der Variante stehen in Laufzeitsicht.

Die Anwendung ist Teil des CAF-Monorepos: eines modularen ABP-/.NET-10-Monolithen mit wiederverwendbaren, anwendungsneutralen Bausteinen (CAF.Modules.Authentication, CAF.Modules.Documents[.Contracts], CAF.Modules.UI.Blazor) und der einzigen aktuell existierenden Anwendung, dem Anhörungsportal, unter applications/RAL/QualityMarks/HearingPortal/src/. Das Portal ist damit gleichzeitig Referenzanwendung für die No-Layer-Architektur des Monorepos und Träger der eigentlichen Fachlogik. Details zu Modulschnitt und Abhängigkeitsrichtung stehen im Bausteinsicht; die noch offenen Bereiche und ihre Nachweislücken benennt Risiken und technische Schulden.

Wesentliche fachliche Ziele

  • Durchgängiger, nachvollziehbarer Anhörungsprozess: Von der Anlage über Vier-Augen-Freigabe, zuverlässigen Versand, Fristen/Erinnerungen, Stellungnahmen und internen Kritikdialog bis zu Bescheidung, Abschluss/Archiv oder kontrolliertem Abbruch — jeder Statuswechsel läuft über benannte Operationen, nie über einen frei setzbaren Status.
  • Dedizierte Institutionskonten statt Linkzugang: RAL legt je Institution dedizierte Benutzerkonten an — mehrere pro Institution (je Kontakt eines, korreliert über den stabilen OIDC-sub), keine Selbstregistrierung, kein anonym erreichbarer Link, kein Rückfallweg, sofort wirksamer Entzug (Bereich „Institutionszugang neu“, US-22/US-22a/US-22b/US-22c). Der frühere Gastlink ist durch RAL-QM-HP-31 ohne Zugriffsrecht stillgelegt; interne Rollen (Administrator, Referent, Geschäftsführung) erhalten nur die ihnen erlaubten Aktionen.
  • Verlässliche Institutions- und Kontaktverwaltung: Institutionen mit Anschrift, Kreis, Pflichtpartner-Kennzeichen und mehreren Kontakten pflegen, samt CSV-Import/-Export und geschütztem Löschen erst nach Nutzungsprüfung.
  • Kontrollierte Kontoprovisionierung: Institutionskontakte und Mitarbeitende werden über explizite, auditierte Wege zu Keycloak-Konten und genau einer Produktrolle zugeordnet — ohne Dublettenkonten oder automatische Rollenvergabe.
  • Betriebsfähigkeit ab Tag eins: Health-Endpunkte, PII-arme Kennzahlen mit Korrelationsverfolgung vom Versandfehler bis zur Nachricht, sowie ein bei jedem Push laufendes automatisches Qualitätsgate.

Qualitätsziele (Top-Prioritäten)

Rang Qualitätsziel Motivation / Ausprägung
1 Sicherheit & Autorisierung Jede geschützte Operation prüft Permission und Objektzugriff serverseitig; eine DocumentId, URL oder ein Dateiname verleiht nie Zugriff. Dokumentzugriffswege (intern und Institutionskonto; Gast- und Direkt-Share-Pfade sind im Host gesperrt) revalidieren bei jedem Zugriff neu; ein versteckter Button ist keine Autorisierung.
2 Nachvollziehbarkeit & Integrität des Verfahrens Freigabe erzwingt eine andere handelnde Person als die Einreichung, sperrt die geprüfte Zusammensetzung über eine Kompositionsprüfsumme; Versand läuft über eine persistente Outbox mit Idempotency Keys statt „best effort“; eine PII-arme fachliche Historie ergänzt das ABP-Audit-Log.
3 Modularität & Wartbarkeit No-Layer-Architektur mit klaren, architekturtestgesicherten Modulgrenzen (Contracts/Runtime/Blazor je Feature); CAF.Modules.* bleiben anwendungsneutral und dürfen nie von der Anwendung abhängen; Cross-Module-Referenzen laufen ausschließlich über IDs und Contracts, nie über fremde Entities oder Tabellen.
4 Verifizierbarkeit / Testtiefe Fach-, Komponenten- und SQL-Server-Tests belegen die Funktionen. Echte Browserprüfungen sind optional und standardmäßig aus; fehlende Browsernachweise werden benannt. Das automatische Qualitätsgate blockiert Pushes bei roten oder übersprungenen Pflichtprüfungen und ausstehenden EF-Modelländerungen.
5 Bedienbarkeit & Barrierefreiheit Einheitliche Cohesion-Oberfläche auf Deutsch, WCAG 2.1 AA (axe, serious/critical als Fehler) über alle Fach- und Portalseiten, responsive und tastaturbedienbar; Datenseiten behandeln Loading/Empty/Error/Forbidden/Validation/Saving/Stale-Concurrency konsistent.

Diese Rangfolge steuert Zielkonflikte: Sicherheits- und Integritätsanforderungen sind nicht verhandelbar und gehen im Zweifel vor Bedienkomfort oder Implementierungsaufwand. Die geforderte Nachweistiefe je Änderungsart steht im Entwicklungsleitfaden §15.1; Qualitätsszenarien mit konkreten Ereignis/Antwort-Paaren stehen im Qualitätsanforderungen.

Stakeholder-Überblick

Rolle Erwartungshaltung
Interne Benutzer (Administrator, Referent, Geschäftsführung) Verfahren anlegen, bearbeiten, freigeben, versenden, bescheiden, abschließen oder abbrechen — jeweils nur mit den ihrer Rolle zugewiesenen Rechten; Administrator und Geschäftsführung dürfen zusätzlich laufende Verfahren abbrechen.
Externe Teilnehmer (Institutionskontakte) Über ihr persönliches, von RAL angelegtes Benutzerkonto ihrer Institution (/portal/zugang-uebernehmen; mehrere Konten je Institution) fristgerecht Stellungnahmen abgeben, eigene Unterlagen einsehen/herunterladen und interne Antworten auf die eigene Kritik sehen — ohne Einblick in fremde Teilnehmerdaten.
Fachadministration / Mitarbeitendenkonten-Verwalter Mitarbeitende über /administration/mitarbeitendenkonten kontrolliert anlegen, genau eine Produktrolle zuweisen, wiedereinladen sowie app-lokal sperren/entsperren.
Betrieb Lokalen Stack (SQL Server, Keycloak, Mailpit) über doctor.cmd/setup.cmd/start.cmd reproduzierbar betreiben; Gesundheitszustand über /health/live und /health/ready, Versand-/Fristenprobleme über PII-freie .NET-Kennzahlen mit Korrelationskennung verfolgen.
Entwicklungsteam Modulgrenzen, No-Layer-Konventionen und das bei jedem Push laufende Qualitätsgate einhalten; verbindliche Regeln stehen im Entwicklungsleitfaden und in conventions/.
Keycloak (technischer Stakeholder) Alleiniger OIDC-Token-Issuer und Identitätsquelle; die Anwendung selbst betreibt keinen lokalen OpenIddict-Server.

Weiterführend: Randbedingungen, Kontextabgrenzung, Lösungsstrategie sowie die Betriebs- und Startpfade in ../GETTING-STARTED.md und im Portal-README.

02. Randbedingungen

Dieses Kapitel fasst die Vorgaben zusammen, innerhalb derer Architektur- und Implementierungs- entscheidungen getroffen werden. Sie sind größtenteils verbindlich gesetzt (Technologie-, Team- und Prozessvorgaben) und nicht das Ergebnis einer freien Architekturwahl. Details zu Ownership und Modulschnitt stehen im Entwicklungsleitfaden; Begründungen je Einzelkonvention in docs/conventions-decisions.md.

1. Technische Randbedingungen

Bereich Vorgabe Bindend seit
Runtime/Sprache .NET 10, C# mit Nullable enable Projektstart
Anwendungsarchitektur ABP Single-Layer-/No-Layer-Template — bewusst kein klassisches 8-Projekt-Layering (Domain.Shared/Domain/Application.Contracts/…); ein Feature besteht aus {Feature}.Contracts + Runtime, optional .Blazor ABP-Vorgabe, siehe AGENTS.md
UI-Technologie Blazor Server, ausschließlich InteractiveServer (kein WebAssembly, kein @rendermode in Feature-Code) Architekturtest-erzwungen
UI-Komponentenbibliothek Cohesion (Syncfusion-Basis) ist verpflichtend für jede CAF-/GZ-Oberfläche; MudBlazor/Syncfusion/Blazorise dürfen im Anwendungscode nicht direkt gerendert werden. Ausnahme: allein die MVC-Razor-Pages unter /Account/** auf dem LeptonX-MVC-Theme. Blazor-seitig wird keine Vendor-Komponente gerendert; Blazorise kommt transitiv ueber ABPs Blazor-Theming-Paket mit und wird im Bundle wieder entfernt AGENTS.md Abschnitt „Architektur der Benutzeroberfläche"
Persistenz EF Core, SQL Server als einziger Provider; PascalCase-Tabellennamen mit Owner-Präfix (RALQM…, CAF…); ein Modul besitzt seine Entities und Configure{Feature}()-Erweiterung, der Host bleibt Migration Host DEVELOPMENT-GUIDE.md Abschnitt 9
Authentifizierung/Autorisierung Keycloak als OIDC-Server via OpenIddict-Client-Integration (CAF.Modules.Authentication); ABP Identity/Permission Management führt lokale Benutzer, Rollen und Permissions. Kein lokaler OpenIddict-Server, kein Feature Management ohne nachgewiesenen Bedarf AGENTS.md
Nicht zugelassene Infrastruktur PostgreSQL, Redis, RabbitMQ, Kafka, Hangfire, Elsa, GraphQL, DMS oder Microservices ohne ausdrückliche Nutzerfreigabe und belegten Use Case DEVELOPMENT-GUIDE.md Abschnitt 11/16
Paketverwaltung Central Package Management (Directory.Packages.props), packageSourceMapping in NuGet.Config (u. a. Cohesion exklusiv auf internem Feed) docs/conventions-decisions.md N-01/N-02
Build-Qualität TreatWarningsAsErrors=true plus .NET-Analyzer; Restore/Build/Test müssen mit null Warnings/null Errors durchlaufen DEVELOPMENT-GUIDE.md Abschnitt 15.2

Diese Vorgaben sind in Architekturtests kodifiziert (z. B. Verbot CAF.Modules.* → CAF.Applications.RAL.QualityMarks.HearingPortal.*, InteractiveServer-only, Cohesion-only-UI) — sie werden nicht gelockert, nur Pfad-Literale bei struktureller Umbenennung nachgezogen.

2. Organisatorische Randbedingungen

Sprache

  • UI-Texte: Deutsch (Produktsprache), über IStringLocalizer<T> und paritätische DE-/EN-Localization-JSON je owning Modul — keine hartkodierten UI- oder Business-Exception-Texte.
  • Code-Identifier und Kommentare: Englisch.
  • Logs: strukturiert und Englisch (ILogger<T> mit Templates, nie String-Interpolation, nie direkte Serilog-Referenz).

Diese Dreiteilung gilt repositoryweit, siehe conventions/baseline.md.

Konventionen und Entscheidungsprozess

  • Themenspezifische Coding-Konventionen liegen in conventions/ (C#, EF Core, Blazor, ABP, Tests, Git, Security, PowerShell, CI/CD, MSBuild/NuGet) und sind bewusst knapp gehalten.
  • Die repo-spezifische Adopt/Reject-Entscheidung je Konvention — inklusive Vor-/Nachteil — steht in docs/conventions-decisions.md; das ist die maßgebliche Quelle, welche geteilte Konvention hier tatsächlich gilt (z. B. explizite Typen statt var und das StyleCop-Set sind bewusst abgelehnt, IClock/IGuidGenerator, ConcurrencyStamp und die Security-Baseline sind übernommen).
  • Quellenpriorität bei Konflikten: aktuelle Benutzeranweisung → nächstgelegene AGENTS.md → repositoryweite Arbeitsgrundlage → aktuelle Architektur/Entwicklungsleitfaden. Nur dieses verlinkte Kernset gibt Repository-Regeln vor; Code und ausführbare Nachweise bestimmen den Implementierungsstand.
  • Strukturelle Änderungen (Modulgrenze, Sicherheit, Persistenz, Abhängigkeit, Betrieb) ziehen Architektur, Arbeitsgrundlage und durchsetzende Tests im selben Änderungssatz mit; es wird keine parallele Entscheidungsdokumentation gepflegt.

Git-Workflow

  • Conventional Commits: feat|fix|refactor|docs|test|chore(scope): subject, imperativ, Subject ≤ 50 Zeichen.
  • main ist geschützt: kein Direct-Push, kein Force-Push; Änderungen ausschließlich per Merge Request mit Review.
  • Branch-Namen kebab-case (feature/…, fix/…, chore/…), nach Merge gelöscht.
  • Betroffene Gates bei jedem Push: versionierte Git-Hooks (tools/git-hooks, aktiviert über setup.cmd) bestimmen aus allen gepushten Ref-Diffs und dem Projektgraphen die betroffenen Apps. Deren serverseitige Tests und übrige Pflichtschritte bleiben verbindlich. Browser-, axe-, Tastatur- und Browser-Berechtigungsprüfungen sind optional und standardmäßig aus, auch beim Push; reine Dokuänderungen prüfen Links und Reporting. Ein rotes, fehlendes oder zum falschen Commit gehörendes Ergebnis blockiert den Push. Der Commit prüft nur den gestageten Diff auf gesperrte Pfade, Secrets und Konfliktmarker. git push --no-verify ist eine dokumentierte Ausnahme, kein Arbeitsweg. Details: DEVELOPMENT-GUIDE.md Abschnitt 15.2 und GETTING-STARTED.md.

Agenten als Mitwirkende

  • Claude Code und Codex arbeiten nach denselben Quellen wie menschliche Entwickler; es gibt bewusst kein tool-spezifisches Regel-Duplikat. Claude Code liest CLAUDE.md, einen reinen Zeiger auf AGENTS.md; Codex liest diese Root-AGENTS.md direkt, weil seine Projekt-Discovery im Repositorywurzelverzeichnis beginnt.
  • Jeder Implementierungs- oder Prüfagent liest vor einer Codeänderung die repositoryweite Arbeitsgrundlage, die aktuelle Architektur, den Entwicklungsleitfaden und die nächstgelegene AGENTS.md.

3. Namens- und Strukturkonventionen (Randbedingung für jede Erweiterung)

  • Anwendungsspezifische persistierte Identifier tragen den Präfix RALQM (Tabellen RALQMHearings/RALQMInstitutions, Error Codes RALQM.<Feature>:, Permission-Gruppe RALQM, Rollen RALQMAdministrator/RALQMCaseOfficer/RALQMManagement).
  • Technische Slugs bleiben lowercase (ral-qualitymarks).
  • Wiederverwendbare CAF-Module behalten den CAF-Präfix (CAFDocuments*).
  • modules/* darf nicht von der Anwendung abhängen — durch Architekturtests erzwungen.
  • Umbenennungen sind stets datenerhaltend; historische EF-Migrationen behalten ihre alten Identifier bei.

Diese Randbedingungen sind Grundlage für die Lösungsstrategie und die Bausteinsicht; technische Details zu Setup und Betrieb stehen im Schnelleinstieg und im Entwicklungsleitfaden.

03. Kontextabgrenzung

Dieses Kapitel zieht die Systemgrenze um das eine fachliche Deployable CAF.Applications.RAL.QualityMarks.HearingPortal (RAL Gütezeichen Anhörungsportal, „RALQM") und benennt seine externen Nachbarn — fachlich (wer interagiert wozu) und technisch (über welche Schnittstelle). Die Modul-interne Gliederung folgt in der Bausteinsicht; die Begründung für „ein Deployable, eine Anwendungsdatenbank" steht in der Lösungsstrategie.

3.1 Systemgrenze

Es gibt genau ein fachliches Anwendungs-Deployable: den RALQM-Host, eine ASP.NET-Core-/Blazor- Web-App mit Interactive Server. Keycloak, SQL Server und der SMTP-Provider sind Infrastruktur — keine weiteren GZ-Fachservices. Die Anwendung besitzt genau einen HearingPortalDbContext und einen Migration-Host im Host-Projekt; Feature-Application-Services laufen im selben Prozess wie die Blazor-UI (kein separater API-Tier). Details zum Ablauf eines Requests innerhalb der Grenze stehen in der Laufzeitsicht.

flowchart TB
    Staff["Interne Mitarbeitende\nAdministrator / CaseOfficer /\nManagement"] --> App
    Guest["Externe Teilnehmer\nInstitutionskonten (mehrere\nBenutzerkonten je Institution)"] --> App
    ShareRecipient["Empfänger eines direkten\nDokumentenlinks"] --> App
    App["RALQM HearingPortal\nBlazor Web App (Interactive Server)"] <-->|"OIDC Code + PKCE"| Keycloak["Keycloak\nIdentity Provider"]
    App -.->|"schmaler Admin-Port"| Keycloak
    App --> GzDb[("RALQM-Anwendungs-\ndatenbank (SQL Server)")]
    App --> Mail["SMTP-Provider\nlokal: Mailpit"]
    Keycloak --> KcDb[("Keycloak-Datenbank\nSQL Server, separat")]

    classDef system stroke-width:3px
    class App system

Lokal liegen die RALQM- und die Keycloak-Datenbank auf derselben SQL-Server-Instanz; fachlich und technisch bleiben es getrennte Datenbanken mit getrennten Principals (Keycloak-eigener Login vs. dedizierter ralqm-Datenbankprincipal für die Anwendung).

3.2 Fachlicher Kontext

Nachbar/Akteur Rolle Fachliche Interaktion
Interne Mitarbeitende — Administrator (RALQMAdministrator) Verwaltungsrolle ohne Verfahrens- und Freigaberechte (seit RAL-QM-HP-18) Stammdaten (Institutionen/Kontakte), Mitarbeitendenkonten und Institutionskonten, Wertelisten, Ablage, Vorlagen, Postausgang und Störungen; keine Fallarbeit im Anhörungsprozess
Interne Mitarbeitende — Sachbearbeitung (RALQMCaseOfficer) vollständige Fallbearbeitung ohne Freigabe Anhörungen anlegen/bearbeiten/einreichen/versenden, Institutionen/Kontakte pflegen, Dialog mit Teilnehmern, Bescheidungsübermittlung (Adjudicate) und Abschluss (Complete) nach RAL-QM-HP-46 Regel 1/O1 und RAL-QM-HP-51 O2
Interne Mitarbeitende — Geschäftsführung (RALQMManagement) Lesezugriff plus Vier-Augen-Entscheidungen Freigabe (Approve) und Nachversand-Freigabe (Supplemental.Approve), dazu Abbruch laufender Verfahren; darf keine eigene Einreichung selbst freigeben; bescheidet und schließt nicht ab
Institutionsvertreter — Institutionskonto angemeldeter Nutzer eines von RAL angelegten Benutzerkontos seiner Institution (mehrere Konten je Institution, je Kontakt eines; Einstieg /portal/zugang-uebernehmen, keine Selbstregistrierung) Konto übernehmen, Dokumente einer Anhörung einsehen, eigene Stellungnahme anlegen/ändern, sieht Meine Teilnahmen (nur die eigene Institution, US-22c)
Empfänger eines direkten Dokumentenlinks anonymer Empfänger eines CAF-Share-Tokens Download eines einzelnen freigegebenen Dokuments über ein befristetes, widerrufbares Secret; kein Ersatz für den Teilnahmezugang über das Institutionskonto
Keycloak Identitätsquelle authentifiziert alle Benutzer zentral; ABP projiziert lokale Benutzer/Rollen über den stabilen OIDC-sub

Objektzugriff, Verfahrensstatus und Teilnehmerzuordnung werden zusätzlich je Feature geprüft; Details zu Rollen, Grants und den vier getrennten Dokument-Zugriffswegen stehen in den querschnittlichen Konzepten (Kapitel 8).

3.3 Technischer Kontext

Nachbar Schnittstelle/Protokoll Zweck Richtung
Endnutzer-Browser HTTPS, Blazor-Interactive-Server-Circuit (SignalR) UI-Rendering und Interaktion; JS-Interop nur nach interaktivem Rendern eingehend
Keycloak (Login) OIDC Authorization Code + PKCE über HTTPS Authentifizierung, Token-Issuer für interne und kontogebundene externe Nutzer bidirektional
Keycloak (Admin-API) schmaler, kontrollierter Admin-Port (CAF.Modules.Authentication-Provisioning) kontrollierte Kontoprovisionierung für Mitarbeitende und Institutionskontakte; scheitert bei mehrdeutiger Identität geschlossen, vergibt nie automatisch eine Fachrolle ausgehend
SQL Server — RALQM-Anwendungsdatenbank Tabular Data Stream (ADO.NET/EF Core) Persistenz über HearingPortalDbContext; Migrationen laufen ausschließlich im Host ausgehend
SQL Server — Keycloak-Datenbank Tabular Data Stream eigene Persistenz von Keycloak; lokal gleiche Instanz, getrennte Datenbank und Principals, keine GZ-Fachdaten Keycloak-intern
SMTP-Provider SMTP Versand der Hearing-Outbox (Einladungen, Nachrichten je Verfahrensschritt) über ABP Emailing; lokal Mailpit als Development-Fänger ausgehend
BLOB-Inhalt (Dokumente) SQL-Server-BLOB-Provider (ABP BLOB Storing Database) physischer Dateiinhalt liegt in derselben RALQM-Anwendungsdatenbank; keine zweite Dokumentablage ausgehend

Lokale Development-Endpunkte (siehe GETTING-STARTED): Der Stack-Descriptor besitzt APP_PORT, KEYCLOAK_PORT (Realm ral), MAILPIT_WEB_PORT, MAILPIT_SMTP_PORT und SQLSERVER_PORT; status.cmd zeigt die wirksamen Adressen. Jeder Worktree-/Branch-Kontext hat eigene Endpunkte. Alle Compose-Ports sind nur an Loopback gebunden (siehe applications/RAL/QualityMarks/HearingPortal/etc/docker/development/docker-compose.yml); Produktionsendpunkte und -zugangsdaten werden außerhalb dieser lokalen Dateien betrieben.

3.4 Bewusst keine weiteren Nachbarn

Die Systemgrenze umfasst keinen zweiten Anwendungsservice, kein externes DMS, keine Microservice-Nachbarn und keine zweite GZ-Datenbank — ein neuer externer Nachbar braucht zuerst einen konkreten, getesteten Anwendungsfall. Details zu bewusst nicht vorhandener Infrastruktur stehen in den Randbedingungen.

04. Lösungsstrategie

Dieses Kapitel bündelt die grundlegenden Entscheidungen, die den Lösungsraum aufspannen und in allen Folgekapiteln als gesetzt vorausgesetzt werden. Die begründete Langform je Entscheidung steht in den Architekturentscheidungen; die Abwägung gegen externe Konventionsvorlagen in docs/conventions-decisions.md.

4.1 Modularer Monolith statt Microservices

Genau ein Deployable (CAF.Applications.RAL.QualityMarks.HearingPortal) und eine Anwendungsdatenbank bilden die gesamte Lösung. Microservices, ein zweites GZ-Datenbankschema, Message-Broker (RabbitMQ/Kafka) oder ein API-Gateway sind bewusst nicht Teil der Architektur (siehe Qualitätsanforderungen §10.3 und Risiken §Bewusst nicht als Schulden geführt). Der Grund ist Betriebsökonomie für ein Fachportal dieser Größe: ein Deploy-Artefakt, eine Migration Pipeline, ein Health-Check. Die fachliche und im Quellcode erzwungene Modultrennung (siehe 4.3) hält die Option auf spätere Auskopplung offen, ohne sie vorwegzunehmen — Cross-Modul-Kommunikation läuft bereits heute über schmale Contracts und IDs, nicht über gemeinsame Entities.

4.2 „No-Layer" statt klassischer 8-Projekt-ABP-Schichtung

Die strikte ABP-Standardaufteilung (Domain.Shared/Domain/Application.Contracts/Application/ EntityFrameworkCore/HttpApi/…​) ist für dieses Repo explizit N/A (conventions-decisions.md ABP-11). Domain, Application und Persistence bleiben logische Schichten, aber materialisiert in einer kompakten Runtime-Assembly je Modul statt in sieben eigenen Projekten. Eine weitere Assembly-Grenze wird erst gezogen, wenn sie unabhängig konsumiert, versioniert oder ausgetauscht werden muss (ADR-01). Das reduziert Projekt- und Verkabelungsaufwand, ohne die fachliche Kapselung aufzugeben — die Kapselung sitzt in Feature-Slices, nicht in Assembly-Zahl.

4.3 Feature-Slices: Contracts / Runtime / Blazor

Jedes Fachmodul zerfällt in bis zu drei Projekte mit einseitiger Abhängigkeitsrichtung:

text
<Modul>.Contracts   DTOs, AppService-Interfaces, Permissions, Localization, öffentliche IDs
<Modul>              Runtime: Domain, Application Services, EF-Mapping
<Modul>.Blazor       optionale Feature-Seiten/Komponenten (konsumiert nur Contracts)
  • Contracts kennt keine EF-Entities und keine Implementierungsdetails; sie ist die einzige Fläche, die andere Module referenzieren dürfen.
  • Runtime implementiert die Contracts, besitzt die eigenen Tabellen und darf nie fremde Runtime, fremde Entities oder fremde DbSets referenzieren.
  • Blazor referenziert die eigene und ggf. fremde Contracts sowie Shell-Contracts, niemals eine Runtime.

Diese Grenzen sind eine Compile-Time-Eigenschaft, durchgesetzt über Architekturtests, nicht nur eine Ordnerkonvention (Abhängigkeitstabelle in Bausteinsicht §5.2.6). Cross-Modul-Referenzen erfolgen ausschließlich über IDs (z. B. InstitutionId, DocumentId), nie über relationale Foreign Keys oder Navigation über Modulgrenzen hinweg (Querschnittliche Konzepte §2).

4.4 Zwei Bausteinklassen: wiederverwendbare CAF-Module vs. GZ-Fachmodule

Der Quellcode trennt zwei fachlich unterschiedliche Verantwortungsebenen strikt:

Klasse Beispiele Kennzeichen
CAF.Modules.* CAF.Modules.Authentication, CAF.Modules.UI.Blazor, CAF.Modules.Documents(.Contracts) anwendungsneutral, keine Abhängigkeit auf CAF.Applications.RAL.QualityMarks.HearingPortal.*, Quellcode-Baustein (nicht packbar) mit verbindlicher VERTRAG.md
GZ-Fachmodule Authorization, Institutions, Hearings (je mit .Contracts/.Blazor) RAL-Gütezeichen-spezifisch, referenzieren Framework-Contracts, tragen die fachlichen Regeln

CAF.Modules.* darf laut ADR-06 keinen GZ-Namespace und keinen GZ-Fachbegriff referenzieren; die Regel gilt nie umgekehrt (CAF.Modules.* → CAF.Applications.RAL.QualityMarks.HearingPortal.* ist verboten). Konkret sichtbar wird das an den Modul-READMEs:

  • modules/Authentication/README.md beschreibt einen OIDC-/PKCE-Client ohne Produktrouten oder Berechtigungen der Anwendung.
  • modules/Documents/README.md beschreibt einen mandantenfähigen Dokument-Adapter über ABP File Management, ebenfalls ohne Abhängigkeit auf das Portal.

Beide Bausteine behalten dabei bewusst das Präfix CAF in ihren persistierten Bezeichnern (CAFDocumentShareGrants), unabhängig vom RALQM-Präfix der Anwendung (siehe 4.5). Diese Trennung macht die Framework-Module potenziell in einem anderen ABP-Host wiederverwendbar, ohne dass GZ- Fachlogik mitgezogen wird.

4.5 RALQM-Präfixstrategie für persistierte Kennungen

Anwendungsspezifische Kennungen tragen konsequent das Präfix RALQM: Tabellen (RALQMHearings, RALQMInstitutions), Fehlercode-Namespaces (RALQM.<Feature>:), die Permission-Gruppe RALQM und die Rollen RALQMAdministrator/RALQMCaseOfficer/ RALQMManagement. Technische Slugs bleiben demgegenüber lowercase (ral-qualitymarks). Wiederverwendbare CAF-Module behalten ihr eigenes CAF-Präfix (CAFDocuments*) und werden von der Umbenennung nicht erfasst — der Präfix markiert Ownership, nicht Technologie. Renames sind grundsätzlich daten­erhaltend: Migrationen, die einen Rename umsetzen (z. B. 20260825174437_RenamePersistedIdentifiersToRALQM), erzeugen keinen Datenverlust, und historische EF-Migrationen behalten ihre alten Identifiers, statt rückwirkend umgeschrieben zu werden (conventions-decisions.md EF-03). Diese Strategie hält die Grenze zwischen „Framework" und „Anwendung" auch im Datenbankschema sichtbar, ohne die Migrationshistorie zu verfälschen.

4.6 Cohesion als verbindliche Fach-UI

Eigene CAF-/GZ-Oberflächen rendern ausschließlich über Cohesion (Syncfusion); MudBlazor, rohes Syncfusion oder Blazorise dürfen von Fachcode nicht direkt aufgerufen werden (conventions-decisions.md BL-01, Querschnittliche Konzepte §5). Ausgenommen sind allein die MVC-Razor-Pages unter /Account/**, die auf dem LeptonX-MVC-Theme bleiben. Blazor-seitig gibt es seit der Ablösung der ABP-Modulansichten keine Ausnahme mehr: kein MudBlazor- und kein Blazor-LeptonX-Paket ist noch referenziert, und keine Vendor-Komponente wird gerendert. Blazorise kommt transitiv ueber ABPs Blazor-Theming-Paket mit; seine Stylesheets werden im globalen Bundle wieder entfernt. Träger dieser Entscheidung ist CAF.Modules.UI.Blazor: zentrale Cohesion-Registrierung, Lizenzprüfung, Assets/Bundles und die Interactive-Server-Policy an einer Stelle. Das Cohesion-Paket kommt aus dem GitLab-Gruppen-Feed; Quelle, Versionsbereich und Auth-Wege stehen in Getting Started, Cohesion-Paketquelle. Feature-Seiten liegen im jeweils owning .Blazor-Projekt, bleiben render-mode-neutral (kein @rendermode in Feature-Code) und erfüllen WCAG 2.1 AA als Pflichtkriterium — fachlich relevant für ein Behördenportal.

4.7 Konsequenzen für Cross-Cutting-Themen

  • Auth: Keycloak bleibt alleiniger Identity Provider; CAF.Modules.Authentication kapselt den OIDC-Client, GZ Authorization komponiert Rollen/Grants aus featureeigenen Permissions (Querschnittliche Konzepte §1).
  • Dokumente: ABP File Management/BLOB Storing bleibt Descriptor- und Inhalts-Owner; CAF.Modules.Documents liefert nur den neutralen Vertrag plus Share-Grants, Hearings trägt die fachliche Bedeutung einer Datei (Laufzeitsicht §6.4).
  • Persistenz: ein gemeinsamer HearingPortalDbContext, aber jedes Modul liefert seine eigene Model-Konfiguration; Migrationen entstehen ausschließlich im Host.
  • Tests: die Modulgrenzen werden nicht nur dokumentiert, sondern durch Architekturtests erzwungen und sind damit Teil des Qualitätsnetzes (Querschnittliche Konzepte §7).

Siehe auch

  • Randbedingungen — die gesetzten Vorgaben, die diese Strategie motivieren.
  • Kontextabgrenzung — Systemgrenze und externe Nachbarn.
  • Bausteinsicht — Detailschnitt der hier benannten Module.
  • Querschnittliche Konzepte — Vertiefung von Auth, Dokumenten und Konfiguration.
  • Architekturentscheidungen — ADR-Langform je Grundsatzentscheidung.
  • docs/conventions-decisions.md — Konventions-Entscheidungsregister mit Status je Regel.
  • DEVELOPMENT-GUIDE.md — verbindliche Arbeitsweise bei Änderungen.

05. Bausteinsicht

Dieses Kapitel zerlegt die statische Struktur des Systems in Bausteine: zuerst auf Ebene 1 (Module/Projekte), dann auf Ebene 2 (Feature-Slices der Anwendung). Motivation und Zielsetzung der No-Layer-Architektur stehen im Kontextabgrenzung und in der Lösungsstrategie; dieses Kapitel beschreibt nur, was existiert und **wer wen abhängen darf. Details zu Laufzeitabläufen liefert die Laufzeitsicht.

Quelle der Wahrheit für alle hier genannten Grenzen sind die Architekturtests (siehe ADR-06); dieses Kapitel fasst sie arc42-konform zusammen und verlinkt auf die READMEs der Bausteine.

5.1 Ebene 1: Gesamtsystem in Module und Anwendung

Das System ist ein modularer Monolith: ein Deployable (CAF.Applications.RAL.QualityMarks.HearingPortal), eine gemeinsame SQL-Server-Datenbank, aber strikt getrennte Bausteine im Quellcode und im Abhängigkeitsgraphen. Dafür gibt es drei Solution-Einstiege: CAF.Framework.slnx enthält die CAF-Projekte und deren Tests. CAF.slnx im Wurzelverzeichnis umfasst das Monorepo und gliedert in /modules/<Modul>/src/, /Applications/RAL/QualityMarks/HearingPortal/ mit je einem Unterordner pro Feature, /modules/<Modul>/tests/, /Tests/Architecture/ und /Tests/Applications/RAL/QualityMarks/HearingPortal/; HearingPortal.slnx umfasst die Anwendung samt CAF-Quellabhängigkeiten und gliedert in /Host & Shell/, /Features/<Name>/, /modules/<Modul>/src/ und /Tests/.

flowchart TB
    subgraph App["Anwendung: RAL Gütezeichen Anhörungsportal"]
        direction TB
        Host["Host\n…RAL.QualityMarks.HearingPortal"]
        Shell["Shell"]
        AppContracts["Contracts"]
        AppUI["UI.Blazor\n(produktweiter UI-Baustein)"]
        Auth["Features/Authorization"]
        Inst["Features/Institutions"]
        Hear["Features/Hearings"]
        VL["Features/ValueLists"]
        Filing["Features/Filing"]
        AbpRepl["ABP-Ersatz-Features\nIdentity, Languages, TextTemplates,\nAuditLogs, Gdpr, Settings\n(Contracts + Blazor; TextTemplates mit Adapter-Runtime,\nSettings mit Absender-Runtime seit RAL-QM-HP-27)"]
    end
    subgraph Modules["Wiederverwendbare CAF-Module\n(anwendungsneutral)"]
        direction TB
        ModAuthn["CAF.Modules.Authentication"]
        ModUI["CAF.Modules.UI.Blazor"]
        ModDocC["CAF.Modules.Documents.Contracts"]
        ModDoc["CAF.Modules.Documents"]
    end

    Host --> Shell
    Host --> Auth
    Host --> Inst
    Host --> Hear
    Host --> VL
    Host --> Filing
    Filing --> ModDocC
    Filing -->|Runtime| Hear
    Hear -->|Blazor| Filing
    Filing --> AppUI
    Host --> AbpRepl
    AbpRepl --> AppUI
    Auth --> Inst
    Auth --> Hear
    Auth --> VL
    Hear --> Inst
    Hear --> VL
    Inst --> VL
    Shell --> AppUI
    Auth --> AppUI
    Inst --> AppUI
    Hear --> AppUI
    VL --> AppUI
    AppUI --> ModUI
    Host --> ModAuthn
    Host --> ModUI
    Auth --> ModDocC
    Hear --> ModDocC
    ModDoc --> ModDocC

Pfeile bedeuten „referenziert/verwendet“ und meinen dabei stets die Contracts des Ziels. Die beiden beschrifteten Pfeile zwischen Filing und Hearings sind deshalb kein Projektzyklus, sondern zwei verschiedene Projekthälften: Filing.Runtime → Hearings.Contracts (Übernahme, Papierkorb) und Hearings.Blazor → Filing.Contracts (Bedien-Einstieg, RAL-QM-HP-16) — Einzelheiten in 5.2.5. Die zentrale, architekturtestlich abgesicherte Regel: die Anwendung hängt von den Modulen ab, nie umgekehrt. Kein CAF.Modules.*-Projekt darf ein CAF.Applications.RAL.QualityMarks.HearingPortal.*-Projekt, einen GZ-Namespace oder einen GZ-Fachbegriff referenzieren.

5.1.1 Anwendungs-Bausteine

Baustein Projekt Verantwortung Grenze
Host CAF.Applications.RAL.QualityMarks.HearingPortal Start, Modulkomposition, Middleware, Routing, konkrete Konfiguration, gemeinsamer HearingPortalDbContext, SQL-Provider, Migration und Seed keine neuen Fachentities, Fachregeln oder Feature-Application-Services
Contracts …HearingPortal.Contracts produktweite Shell-Localization (Ressource RALQM), globale Menünamen, von der Shell benötigte ABP-Contracts keine Feature-Entities, kein EF, keine Runtime-Abhängigkeiten
Shell …HearingPortal.Shell serverorientierte Razor-Class-Library: Startseite (Arbeitsbereich: Einstiegskarten, jede unter dem Recht ihrer Zielseite aus den Feature-Contracts — seit RAL-QM-HP-1 auch „Mitarbeitendenkonten“ unter RALQM.Accounts —, darunter die registrierten Panels über HearingPortalWorkspaceOptions), Anmeldeeinstieg, Pflichtseiten, Produktmenü-Komposition (kein WebAssembly-Client — Render Mode wählt der Host) keine Fachpersistenz, keine Fachregeln, keine browserseitige WASM-Runtime; keine Referenz auf eine Feature-Runtime oder ein Feature-*.Blazor (Regel ClientToFeatureRuntime) — Feature-Inhalt erreicht die Startseite nur über den Erweiterungspunkt in Contracts
UI.Blazor …HearingPortal.UI.Blazor produktweiter Shared UI Building Block (Stufe 6, Designdurchgang): Seitengerüst HearingPortalPage, Filterleiste HearingPortalFilterBar, Statusbadges mit der Ton-Zuordnung HearingPortalStatusTone, Verfahrens-Stepper HearingWorkflowStepper — Produktwerte, die Shell und alle Feature-*.Blazor gleich brauchen keine Route, keine Application-Service-Aufrufe, keine Feature-Runtime; referenziert nur Contracts, Hearings-Contracts (Statusenums) und CAF.Modules.UI.Blazor; Texte kommen aus den Ressourcen der aufrufenden Seite
Features (fachlich) Features/{Authorization,Hearings,Institutions,ValueLists,Filing,Consents} die sechs fachlichen Slices der Anwendung, siehe 5.2 siehe Abhängigkeitstabelle unten
Features (ABP-Ersatz) Features/{Identity,Languages,TextTemplates,AuditLogs,Gdpr,Settings} Cohesion-Produktseiten, die die mitgelieferten ABP-Modulseiten ablösen; je Feature Contracts + Blazor; TextTemplates ergänzt eine schmale Runtime für verwaltete native Inhalte und Schreibschutz; Settings ergänzt seit RAL-QM-HP-27 eine Runtime für die Absenderangaben der Organisation (sieben ABP-Settings der Gruppe RALQM.Organisation, Recht RALQM.Settings.Organisation, Dienst OrganisationSenderAppService, Lese-Port IOrganisationSenderReader für Nachrichten und Anschreiben, Protokoll-Allowlist ProtocolledSettingsOptions). Fachliche Vorlagenregeln bleiben in Hearings, Inhaltsspeicherung in ABP keine eigenen Entities, kein EF-Mapping; referenzieren Produkt-Shell-Contracts und Produkt UI.Blazor (siehe Abhängigkeitstabelle unten)

Details zur Anwendungsstruktur: ../../applications/RAL/QualityMarks/HearingPortal/README.md.

5.1.2 Wiederverwendbare CAF-Module

Alle vier Bausteine liegen unter modules/ und sind Quellcode-Bausteine, keine NuGet-Pakete (IsPackable=false, keine PackageId/Version). Der Host konsumiert sie ausschließlich über ProjectReference. Je Baustein gilt trotzdem die Vertragsdatei VERTRAG.md als Quelltext-Vertrag — arch-getestet, siehe ADR-03.

Die folgende Tabelle fasst zusammen, wie dieses Produkt die Bausteine nutzt. Maßgeblich für Zweck, Abgrenzung und zugesicherte Fläche der Bausteine selbst ist docs/ bzw. die VERTRAG.md am jeweiligen Baustein — diese Tabelle ersetzt beides nicht.

Modul Projekt(e) Verantwortung Grenze
Authentication CAF.Modules.Authentication OIDC-Client (Authorization Code + PKCE), Claim-Mapping, Cookie-/Session-Policy, lokale/remote Abmeldung samt idempotentem Entzug der ABP-Sitzung (RAL-QM-HP-92), Security-Stamp-Revalidierung keine Produktrouten, keine Berechtigungen der Anwendung, keine Realm-/GZ-Rollen-Policy
UI.Blazor CAF.Modules.UI.Blazor zentrale Cohesion-Registrierung (AddCohesion), Lizenzprüfung, Bundle-Contributors für CSS/JS, CAFCohesionSmokePanel keine Produktroute, keine Fachkomponente, kein Render-Mode-Entscheid des Hosts; keine Cohesion-Fassade für Razor-UI-Assemblies (siehe 5.1.3)
Documents.Contracts CAF.Modules.Documents.Contracts DocumentId, vendor-neutrale DTOs, Dokument-/Content-/Share-Interfaces (seit RAL-QM-HP-21 auch IDocumentContentPort.DescribeManyAsync und IDocumentUploadPolicyReader), Permissions, Localization exportiert keine Volo.FileManagement-Typen
Documents CAF.Modules.Documents (Runtime) dünner Adapter über ABP File Management: validierter Dateiinhalt (seit RAL-QM-HP-21 inhaltsbasiert per Dateisignatur, Dateiarten-Liste als Teilmenge der unterstützten Tabelle konfigurierbar), Metadatenzugriff, sichere direkte Freigaben; besitzt nur CAFDocumentShareGrants; Optionsbaum Storage (Datenbank oder Dateisystem außerhalb des Auslieferungsverzeichnisses), den der Host für den Blob-Container liest ABP besitzt Descriptor und BLOB-Inhalt

RAL-QM-HP-19: Features/Filing besitzt die verfahrensunabhängige persönliche/gemeinsame Ablage. Seine Contracts und Runtime konsumieren den neutralen IDocumentFilingPort; ABP besitzt weiterhin sämtliche aktuellen Datei-/Ordnermetadaten und Inhalte. Filing speichert nur Bereichseigentum, Elementzuordnung, Papierkorbzustand und Fachhistorie in RALQMFilingAreas, RALQMFilingEntries und RALQMFilingHistory. Aktive interne Referenten und Produktadministratoren erhalten Read/Manage/Purge für gemeinsame und eigene persönliche Bereiche; fremde persönliche Bereiche bleiben privat. Der Authorization-Port prüft die aktuelle Kontoeignung zusätzlich zu den ABP-Grants. Filing.Blazor bindet den separat paketierten CohesionDataBrowser unter /filing an die Filing-Contracts an. /filing/trash verwendet eine semantische Tabelle mit Cohesion-Aktionen für getrennte Herkunfts-/Löschspalten. Die administrative Bereichsübertragung unter /administration/ablage-uebertragen konsumiert nur Konten- und Bereichsprojektionen, niemals Datei-Metadaten oder Inhalte. Die RCL hat keine Runtime-/EF-Abhängigkeit; CAF-Documents-Contracts liefern lediglich Fehlercodes und deren Localization. Der schmale IHearingDocumentTrashPort ergänzt entfernte Verfahrensdateien aus RAL-QM-HP-21. Hearings besitzt deren Wiederherstellung, Löschauftrag und erhaltene Historie; die Filing-Projektion legt keine zusätzlichen Fachdateien oder Bereiche an. Die gemeinsame Liste sortiert und paginiert erst nach der Zusammenführung der autorisierten Quellen. Ist das ursprüngliche Zweckfenster geschlossen, autorisiert derselbe Hearings-Port die zurückgezogene Quelle für eine unabhängige Kopie in die Ablage. Filing besitzt Zielordner, Namenskonflikte, Operation-ID und Kopierhistorie; CAF erzeugt eine neue Descriptor-/Blob-Identität. Die Quellprüfung hält ihre Sperre in der von Filing begonnenen Serializable-Unit-of-Work bis zum Kopierabschluss. Verfahrensreferenz, Originalinhalt, Status und Grants werden dabei nicht reaktiviert oder verändert. Die Gegenrichtung — eine Ablagedatei als eigenständige Verfahrensfassung übernehmen — ist mit RAL-QM-HP-16 hinzugekommen und in 5.2.5 beschrieben. Siehe die abgestimmte Spec.

Persistierte Kennungen der Module behalten das Präfix CAF (CAFDocuments*), unabhängig vom RALQM-Präfix der Anwendung. Details je Modul: Authentication, Documents, UI.Blazor.

5.1.3 Cohesion-Referenz-Ausnahme für Razor-UI-Assemblies

Die Razor-UI-Assemblies dieser Anwendung — die Produkt-Shell, der produktweite UI-Baustein …HearingPortal.UI.Blazor und jedes Features/*/Blazor — referenzieren das Cohesion-Paket direkt per PackageReference und gerade nicht über CAF.Modules.UI.Blazor. Sie erzwingen dazu RZ10012 als Fehler (WarningsAsErrors), damit ein unaufgelöstes Cohesion-Tag den Build bricht statt still als leeres HTML-Element zu rendern. CAF.Modules.UI.Blazor selbst binden sie für etwas anderes ein: die Grid-Naht (CAFGridDataAdaptor, CAFGridToolbar) und den Fristbaustein (CAFDeadlineChip) — CAF-Konventionen, keine Cohesion-Fassade. Der Host bindet es zusätzlich für Registrierung, Lizenzierung und Bundles ein.

Das ist eine bewusste, Razor-bedingte Ausnahme und keine aufzuräumende Redundanz — Regel, Begründung (Compile-Time-Razor-Assets) und Rollenteilung stehen in docs/ §02 Bausteine. Verbindliche Arbeitsfassung: modules/AGENTS.md und Shell/AGENTS.md.

5.2 Ebene 2: Feature-Slices der Anwendung

Dieser Abschnitt beschreibt die fachlichen Features; die sechs ABP-Ersatz-Features aus 5.1.1 haben keine eigene Runtime und deshalb keine Ebene-2-Struktur.

Jedes fachliche Feature gliedert sich intern in bis zu drei Teilprojekte — Contracts, Runtime und optional Blazor. Diese Dreiteilung besteht nur dort, wo die Grenze echten Nutzen stiftet: Abhängigkeitsrichtung, serverseitige Interception und spätere Auskopplung. Weitere Layerprojekte (z. B. separates Domain/Application) werden nicht vorsorglich angelegt — Domain, Application und EF-Persistenz bleiben logische Schichten innerhalb der Runtime-Assembly.

text
Features/<Feature>/
  Contracts/   DTOs, AppService-Interfaces, Permissions, Localization, öffentliche IDs
  Runtime/     Aggregate, Application Services, EF-Mapping (Domain/Application/EF in einer Assembly)
  Blazor/      optionale Razor-Class-Library: Seiten, Komponenten, Navigation (nur bei eigener UI)

5.2.1 Authorization

Teilprojekt Verantwortung
Authorization.Contracts globale GZ-Permissiongruppe RALQM, Rollenbezeichnungen (RALQMAdministrator, RALQMCaseOfficer, RALQMManagement, seit RAL-QM-HP-18 RALQMInstitution), die drei Kontenrechte RALQM.Accounts/.Invite/.InviteInstitution, Konten-Contracts (Übersicht mit Facetten, Institutionskonto aus Ansprechpartner, Kennungswechsel, Wiedereinladung, Papierkorb/Wiederherstellen, Verlauf — kein Delete); seit RAL-QM-HP-8 der schmale, permissionfreie In-Process-Lesevertrag IStaffAccountReferenceReader (aktive Referenten als Übertragungsziele, Anzeigenamen zu Benutzer-Ids) — dasselbe Muster wie IInstitutionReferenceReader, nie über HTTP
Authorization (Runtime) Rollen-/Grant-Komposition für die vier Rollen mit exaktem Seed-Abgleich, lokale ABP-Benutzerprojektion, kontrollierter Keycloak-Admin-Adapter (anlegen, Einladung, Sperren, Logout, Kennung ändern, Zustände lesen), Mitarbeitenden-Onboarding über bestätigtes Keycloak-Subject (sub), Institutionsbindung (RALQMInstitutionAccountBindings), Kontoverlauf (RALQMStaffAccountHistory), Sitzungswiderruf über den Host-Port IAccountSessionRevoker, zweiter Faktor (RAL-QM-HP-11: Faktorstatus, Selbstbedienung über Keycloak-Aktionen, Rücksetzaufträge RALQMSecondFactorResetOrders)
Authorization.Blazor eigener Kontenpfad: Übersicht mit Facetten (Status, Rolle, Institution), Anlage interner Konten, Wiedereinladung, Produktrollenwechsel, Sperren/Entsperren, Kennungswechsel, Verlauf, Papierkorb mit Wiederherstellen — je Recht des Aufrufers; Institutionskonten entstehen auf der Kontaktseite von Institutions

Die Authorization-Runtime referenziert Feature-Contracts von Institutions und Hearings, um Rollen zu komponieren, besitzt aber keine Feature-Permissions und keine Hearing-Daten. Der Worker-Rechtecheck konsumiert Filing- und Hearings-Permissions; deren Contracts-Module sind deshalb auch im ABP-Modulgraph über DependsOn geladen, ohne die Hearings-Runtime zu laden.

Für RAL-QM-HP-43 prüft IStaffResponsibilityReservation.IsActiveHolderAsync neben Aktivstatus und genau einer internen Produktrolle die eindeutige lokale Keycloak-Subject-Bindung. Auch Papierkorbprojektionen zählen beim Subject-Abgleich; mehrdeutige Importdaten machen das Konto als Erinnerungs-/Eskalationsempfänger ungeeignet. Hearings liest nur den Contract.

5.2.2 Institutions

Teilprojekt Verantwortung
Institutions.Contracts Institution-/Kontakt-DTOs, AppService-Interfaces, Permissions, Localization, Menünamen; die schmalen, permissionfreien In-Process-Leseverträge IInstitutionReferenceReader (Institutionsreferenzen) und seit RAL-QM-HP-9 IInstitutionContactDeputyReader (wirksame Vertretung eines Kontakts zu einem Zeitpunkt, für die Vertretungskopie im Postausgang; permissionfrei, weil Erinnerungen und Fristabläufe ohne angemeldeten Benutzer entstehen) und seit RAL-QM-HP-101 IInstitutionContactLanguageReader (gespeicherte Kommunikationssprache eines Kontakts, für die Sprache je Postausgangszeile und Eingangsbestätigung; permissionfrei aus demselben Grund) — alle drei [RemoteService(false)], nie über HTTP
Institutions (Runtime) Institution-/Kontakt-Aggregat, Aktivität, Kreis (Fach-/Verkehrskreis) und Pflichtpartner-Kennzeichen als eigenes Merkmal (RAL-QM-HP-10 O4), Anschrift, interne Notiz (nur für interne Benutzer, Export nur mit ausdruecklichem Opt-in nach RAL-QM-HP-49; RAL-QM-HP-10), CSV-Import/-Export, Nutzungsprüfung, Dublettenregel für Namen (InstitutionNameDuplicateRule, eine Stelle; RAL-QM-HP-10), Standardansprechpartner (höchstens einer je Institution, immer aktiv, der erste aktive selbsttätig, weder deaktivier- noch entfernbar) und Vertretung am Kontakt mit ihren Zuweisungsregeln und ihrem Leser ohne Authorize (InstitutionContactDeputyReader, Zeitfenster und Aktivität aus Institution.FindEffectiveDeputy, „nicht gefunden“ ergibt keine Vertretung) sowie der Pflichtpartner-Guard beim Setzen des Kennzeichens (RAL-QM-HP-9), Institutionsgruppe mit ihren Mitgliedschaften (RAL-QM-HP-3), EF-Mapping; besitzt RALQMInstitutions, RALQMInstitutionContacts, RALQMInstitutionGroups, RALQMInstitutionGroupMembers
Institutions.Blazor Institutionenliste mit Pflichtpartner-Filter und -Badge (RAL-QM-HP-10), Bearbeitung mit Pflichtpartner-Schalter neben dem Kreis und Dublettenwarnung vor dem Speichern, Bestätigungsdialog vor der Deaktivierung mit der Zahl laufender Verfahren in Liste und Maske (RAL-QM-HP-10), Kontakte mit Telefon, Standard-Kennzeichen und „Als Standard festlegen“, Nutzungshinweis je Kontakt, Bestätigungsdialog mit Kontakten und Verfahren und Vertretung mit Zeitraum (RAL-QM-HP-9), Kontaktsuche mit Standard-Kennzeichen, berechtigte Kontoeinladung; Gruppenübersicht, Gruppenmaske mit Mitgliederliste und Zuordnungsmaske mit Mehrfachauswahl (RAL-QM-HP-3)

Institutionsgruppen (RAL-QM-HP-3): Eine Institutionsgruppe ist ein verfahrensübergreifend wiederverwendbarer, benannter Beteiligtenkreis, gemeinsames Stammdatum aller internen Konten. Gelesen und gepflegt wird sie über IInstitutionGroupAppService (Übersicht mit Freitextsuche über Name und Beschreibung, Gruppe und Mitgliederliste lesen, anlegen, umbenennen, beschreiben, auflösen, Institutionen zuordnen und entfernen); der Name ist eindeutig, Änderungen tragen den Concurrency-Stamp. Die Oberfläche in Institutions.Blazor besteht aus der Gruppenübersicht /institutions/gruppen, der Gruppenmaske /institutions/gruppen/{id} mit Mitgliederliste und Bestätigungsdialog vor dem Auflösen, der Zuordnungsmaske /institutions/gruppen/{id}/zuordnen und dem Menüeintrag „Gruppen“ im Modul Institutionen (neben „Übersicht“ und „Kontaktsuche“; die Seite selbst heißt weiter „Institutionsgruppen“). Ein Mitgliederrefresh übernimmt einen neuen Gruppenstempel nur bei unveränderten Metadaten des Formular-Ausgangsstands. Andernfalls bleiben lokale Eingaben und alter Stempel erhalten und die Maske meldet den Konflikt. Die Zuordnungsmaske ist die erste Mehrfachauswahl des Portals: sie zeigt die Institutionsliste mit deren Suche und Filtern, hält die Auswahl aber selbst — Id und Name, über Seiten und Filterwechsel hinweg, mit sichtbarem Zähler, aufklappbarer Namensliste und einmaligem Speichern. Nach erfolgreichem Speichern entfernt sie nur die übermittelten IDs aus dem Korb; Auswahlen während des laufenden Aufrufs bleiben für den nächsten Vorgang erhalten. Die Sammelauswahl im Tabellenkopf greift höchstens auf die sichtbare Seite. Das Muster steht in grid-muster.md §11, weil es für die nächste Mehrfachauswahl gilt. Das Datenmodell liegt im Slice: das Aggregat InstitutionGroup mit Name, gespeichertem Vergleichsschlüssel, optionaler Beschreibung und Revisionszähler sowie die Mitgliedschaft InstitutionGroupMember als reine Kindentität; Tabellen RALQMInstitutionGroups und RALQMInstitutionGroupMembers. Die Persistenzregeln stehen in Kapitel 8 §2, die Rechte in §1. Das Einbinden einer Gruppe in ein Verfahren (US-13) leistet seit RAL-QM-HP-5 (AC-8) der Baustein Hearings über den Lesevertrag IInstitutionGroupAppService – Gruppe und alle Mitgliederseiten, mit dem Leserecht RALQM.InstitutionGroups (siehe §5.2.3). Für diesen Aufrufer erhielt der Listenvertrag IInstitutionAppService.GetListAsync einen Id-Filter (GetInstitutionsInput.Ids, höchstens MaxIds = 1000): null schränkt nicht ein, eine leere Liste trifft nichts – ein Aufrufer, dessen Id-Menge leer läuft, darf nicht stattdessen alle Institutionen erhalten –, eine unbekannte Id fehlt in der Antwort. So löst Hearings die Institutionen einer Mitgliederseite mit einer Lesung auf statt mit einer je Mitglied; der Filter greift in InstitutionListSearchQuery und ist dort gegen echten SQL Server geprüft (InstitutionSqlServerTests). Sonst bleibt Institutions unberührt. Rundnachrichten an eine Gruppe (RAL-QM-HP-77, ADR-18) liegen in Hearings und lesen Gruppen und Kontakte nur über diese Verträge. Siehe auch Glossar.

Institutions kennt Hearings nur über einen von außen implementierten Nutzungsport (keine Rückabhängigkeit). IInstitutionUsageProvider beantwortet zwei Fragen: ob eine Institution irgendwo referenziert ist (Löschschutz, zählt jede jemals vorhandene Teilnehmer-Referenz) und in wie vielen laufenden Verfahren sie beteiligt ist (Deaktivierungswarnung, RAL-QM-HP-10 A4: weder abgeschlossen noch abgebrochen, als Antragsteller oder nicht zurückgezogener Teilnehmer, je Verfahren einmal). Beide Antworten gibt der Slice Hearings; ohne ihn antwortet die Institutions-Runtime „nicht referenziert“ und „null“. Für Kontakte beantwortet IInstitutionContactUsageProvider zwei Fragen mit bewusst verschiedenen Zählregeln (RAL-QM-HP-9 A4, N3): Der Löschschutz zählt jede jemals vorhandene Beteiligung des Kontakts, auch zurückgezogene und solche in abgeschlossenen Verfahren, samt aktiver Portalkontobindungen, und die erste gemeldete Nutzung blockt; die Nennung laufender Verfahren je Kontakt (Kennung, Referenznummer, Titel; serverseitig die Leseoperation für den Nutzungshinweis und die Abweisung ohne Deaktivierungsbestätigung, RAL-QM-HP-9 AC-4/AC-5; die Anzeige in der Institutionsmaske folgt) zählt nur Verfahren, die weder abgeschlossen noch abgebrochen sind (Entwürfe eingeschlossen), mit nicht zurückgezogener Beteiligung, und führt die Antworten aller registrierten Anbieter zusammen.

RAL-QM-HP-49 erweitert den vorhandenen CSV-Vertrag um alle aktiven Kontakte, den Institutionsstatus und ausdrücklich gewählte interne Notizen. Listen- und Exportabfrage teilen Suche, Filter und Sortierung; der Export hat keine Seitengrenze. Der Import validiert eine Institution samt Kontakten vor dem Schreiben und legt nur neue Namen an. Bekannte Namen werden übersprungen. Länder und Kontaktfunktionen werden über ValueLists.Contracts aufgelöst. Die optionale Institutionsnotiz bleibt intern und nutzt die vorhandenen Pflege-Permissions und den Concurrency-Stamp des Aggregats; ihre Persistenz- und Auditgrenze beschreibt Kapitel 8.

Der Kontaktblock trägt seit Teil 2 auch Anredeform, Titel, Telefonnummer und das Standardkennzeichen aus RAL-QM-HP-9. Jede dort ergänzte Spalte trägt das Präfix Kontakt, weil der Blockerkenner jeden gleichlautenden Kopfnamen als Kontaktspalte liest und ein bloßes Telefon eine Altdatei mit gleichnamiger Institutionsspalte abweisen würde. Das Standardkennzeichen steht je Block; nennt die Zeile keines, bleibt die Automatik aus RAL-QM-HP-9 zuständig. Der Import legt die Kontakte an, bevor der Pflichtpartner-Guard greift; die Kennzeichnung aus der Datei folgt danach und überschreibt die Automatik. Vertretung und inaktive Kontakte sind nicht Teil des Dateivertrags.

RAL-QM-HP-41 ergänzt den Vertrag IInstitutionAppService um „einen Kontakt hinzufügen“ (AddContactAsync mit AddInstitutionContactDto). Der Aufrufer reicht eine Person und den gelesenen Stand der Institution; die Institution legt genau einen aktiven Kontakt hinter den vorhandenen an und antwortet mit ihm. Es gelten die Regeln einer Kontaktzeile der Kontaktpflege (Funktion aus der aktiven Werteliste, zustellbare Adresse, Längen) und ihr Recht RALQM.Institutions.Update. Der Standardkontakt bleibt; hat die Institution keinen, greift die Automatik aus RAL-QM-HP-9. Zwei Kontakte derselben Institution dürfen dieselbe Adresse tragen. Ob eine deaktivierte Institution einen Kontakt erhalten darf, entscheidet der Aufrufer. Einziger Aufrufer ist die Übernahme einer Kontaktmeldung in Hearings (siehe §5.2.3); die Institutionsmaske speichert weiter die ganze Institution.

5.2.3 Hearings

Teilprojekt Verantwortung
Hearings.Contracts DTOs/Interfaces für Anhörungen, Teilnehmer, Dokumente, Freigabe, Versand, Portal, Rückmeldungen (seit RAL-QM-HP-38 die Kategorieregeln HearingResponseRules – fünf Kategorien, Einwand-Menge, 15 Zeichen –, damit Aggregat, beide Erfassungswege und die Masken dieselbe Regel lesen, und der Portalvertrag mit Dateien in einem Aufruf; seit RAL-QM-HP-40 ergänzt eine Auftragskennung die unveränderliche Erstabgabe und neue Beiträge), Fristen, Historie (seit RAL-QM-HP-45 auch die verfahrensübergreifende Recherche IHearingHistorySearchAppService und die Höchstzahl der Ausleitung IHearingHistoryExportSettingsAppService), Einstellungen (seit RAL-QM-HP-38 die Anlagen-Höchstzahl IHearingResponseAttachmentSettingsAppService); Feature-Permissions/Localization
Hearings (Runtime) Anhörungs-Aggregat und Lebenszyklus (siehe Laufzeitsicht), Referenznummern, Teilnehmer mit Beteiligungsgrund (seit RAL-QM-HP-5: Pflichtpartner oder manuell, im Moment der Zuordnung aus dem Kennzeichen der Institution abgeleitet, danach unveränderlich; die Regel HearingParticipationReasonRule liegt im Contract, damit jeder Zuordnungsweg dieselbe anwendet), Gruppenzuordnung (seit RAL-QM-HP-5 AC-8: HearingParticipantAppService.PreviewGroupAssignmentAsync und AssignGroupAsync lesen Gruppe und alle Mitgliederseiten über IInstitutionGroupAppService – der Vertrag von Institutions, kein zweiter Port –, lösen die Institutionen einer Mitgliederseite mit einer Lesung über den Id-Filter von IInstitutionAppService.GetListAsync auf (nie eine Lesung je Mitglied; der Plan läuft für Vorschau, Bestätigung und jede Wiederholung), weisen beide außerhalb des Entwurfs vor dem ersten Gruppenzugriff ab und legen im Entwurf in einem Speichervorgang je zuordenbarem Mitglied eine Beteiligung mit Standardansprechpartner, Grund nach E2 und Pflichtunterlagen an; bereits aktiv beteiligte, deaktivierte oder gelöschte Institutionen und solche ohne aktiven Standardansprechpartner werden mit Grund übersprungen, die Bestätigung rechnet neu; nötig sind Participants.Manage und RALQM.InstitutionGroups), Zugangsentzug je Beteiligung (seit RAL-QM-HP-5 AC-10, E6: Hearing.RevokeParticipantAccess und RestoreParticipantAccess setzen und löschen AccessRevokedAtUtc nur in Dispatched und Adjudication, nur für aktive Beteiligungen, ohne Begründung und ohne Nachricht; die Beteiligung mit Empfänger, Grants und Fristen bleibt, nach dem Ende ist der Zustand eingefroren; je Wechsel ein Verlaufseintrag AccessRevoked/AccessRestored; Participants.Manage), Bestätigung von Antragsteller- und Teilnehmer-Institutionen über den Institutions-Vertrag (Existenz und Aktivität; seit RAL-QM-HP-10 auch der Antragsteller beim Anlegen), Kontaktwahl bei automatischer Einbindung von Pflichtpartnern und beim Kopieren eines Verfahrens über das Standard-Kennzeichen des Kontakts (seit RAL-QM-HP-9; beim Kopieren bleibt der zuvor gewählte aktive Kontakt vorn, E10), Antwort auf den Institutions-Nutzungsport (Löschschutz, Zählung laufender Verfahren, Nennung laufender Verfahren je Kontakt), Dokumentzuordnung/-versionen/-grants (seit RAL-QM-HP-20 auch die Sammelzuordnung: Hearing.GrantDocumentToAllActiveParticipants erteilt in einem Speichervorgang je beim bestätigten Speichern aktiver Beteiligung ohne aktiven Grant einen neuen, lässt bestehende Grants unverändert und merkt sich den Zeitpunkt in HearingDocument.GrantedToAllParticipantsAtUtc; später hinzugefügte Beteiligte erhalten nichts automatisch und werden über ParticipantsMissingBulkGrant benannt; Recht Participants.ManageGrants, Stempelprüfung vor jeder Änderung, ein DocumentGranted-Verlaufseintrag je neu erzeugtem Grant), Vier-Augen-Freigabe (seit RAL-QM-HP-25 mit dem dauerhaften Benachrichtigungsauftrag je Entscheidung an den zuständigen Referenten: HearingApprovalDecisionNotification, Zustellung durch HearingApprovalDecisionNotificationProcessor, Fehler als Störung Notification:ApprovalDecision; seit RAL-QM-HP-22 mit der Zielgruppe Geschäftsführung für Einreichung und Rückzug, der Empfängerprüfung HearingRecipientValidator beim Einreichen und vor dem Erstversand sowie der Lesung GetSubmissionReadinessAsync für die Einreichen-Seite; seit RAL-QM-HP-6 bescheinigt die Prüfung die operative Adresse der Beteiligung), Empfängerstand je Beteiligung (seit RAL-QM-HP-6: die Snapshot-Spalten der Beteiligung – Institution mit Anschrift, Kontakt mit Anrede, Titel, Name, Funktion und E-Mail – sind der operative Stand jeder neuen Aussendung; eine Stammdatenänderung ist eine feldweise Abweichung, die HearingParticipantAppService.GetRecipientStandAsync über den Institutions-Vertrag ausweist, und erreicht den Stand nur über die drei Befehle UpdateRecipientStandAsync (Übernahme, gebunden an den ComparisonStamp der Vorschau), ChangeRecipientContactAsync (anderer aktiver Kontakt derselben Institution) und CorrectRecipientEmailAsync (nur die Adresse) – Recht Participants.Manage, nur in Draft, Dispatched, Adjudication, je ein Verlaufseintrag mit Feldnamen bzw. Kontaktpaar; jede Postausgangszeile friert den Stand ihres Moments ein, siehe Querschnitt), Kommunikationsschritt je Zweck (seit RAL-QM-HP-24: HearingCommunicationStep als Kind des Aggregats – einmalige Kopie des gebundenen Vorlagentexts mit Institutionsabweichungen, gesperrt nach Status und ab der ersten Postausgangszeile; seit RAL-QM-HP-101 je Verfahren, Zweck und Sprache – Editor nach dem Muster Bezugssprache lesend/Zielsprache bearbeitbar, nur die Sprachen der Empfänger, ohne eigenen Text Spiegel des aktuellen Standardtexts (E13), Rückfall am Schritt gekennzeichnet, OutputStamp über alle Sprachstände; IHearingCommunicationStepAppService mit Lesen (Default), Initialisieren/Text/Abweichung (Update), Endfassung je Empfänger als reine Lesung mit OutputStamp, Begleitdokument und ZIP-Zusammenstellung (Documents.Download); Begleitdokument je Teilnehmerzeile als PDF über IDocumentContentPort, Versand mit Anhang, versendeter Wortlaut auf der Zeile, Bounce-Ingest RecordBounceAsync (Outbox.Manage) ohne realen Eingangsweg), Vorlagen, Postausgang (seit RAL-QM-HP-9 mit der Vertretungskopie: HearingOutboxManager liest beim Einstellen jeder Aussendung die wirksame Vertretung über IInstitutionContactDeputyReader und schreibt eine zweite Zeile an ihre Adresse; seit RAL-QM-HP-101 friert jede Zeile die wirksame Sprache ihres Empfängers (Kontaktsprache über IInstitutionContactLanguageReader, sonst Standardsprache; der Renderer löst sie auf) und die Sprache des gesendeten Textes ein, siehe Kommunikationsvorlagen und Verfahrensstände), Einladungen, Portalzugang über den frischen IInstitutionAccountAccessReader aus Authorization.Contracts (RAL-QM-HP-31; ohne Gast- oder Claim-Fallback; seit RAL-QM-HP-30 lesend auch nach dem Ende eines versandten Verfahrens mit Freigaben, die aktiv oder allein durch das Ende widerrufen sind, Schreiben nur in Dispatched, eigener Code PortalAccessMissing ohne Bindung, und die Hearings-eigene HearingPortalParticipationRouteMiddleware für die neutrale 404 der Seitenadresse – siehe Laufzeitsicht 6.4), Stellungnahmen (seit RAL-QM-HP-38: vier Kategorien plus Verzicht mit umgeschriebenen Altwerten, Einwand-Menge an Rückfrage, Abschluss und Bescheidung; Erstabgabe und seit RAL-QM-HP-40 eigenständige Ergänzung als ein Aufruf in zwei Phasen – Vorprüfung, Dateien lesen, Speicherphase in HearingPortalWriteTransaction mit Wiederholung HearingResponseStorageRetry bei einem Deadlock-Opfer –, zweite Erstabgabe über den eindeutigen Index HearingResponseUniqueIndex, festgeschriebene Inhalte und Anlagenzuordnungen unveränderlich, Grenzen aus IDocumentUploadPolicyReader und der Einstellung ResponseAttachmentMaxCount; siehe Laufzeitsicht 6.4 und Querschnitt), fällige Aktionen, Dashboard, Historie — seit RAL-QM-HP-45 mit der verfahrensübergreifenden Recherche (Filter, Zählung, Sortierung und Seitenwahl in SQL, Namen und Feld-Diffs je Seite über das geteilte Lesemodell HearingHistoryReadModel) und deren CSV-Ausleitung (Exportrecht des Moduls, Höchstzahl als protokolliertes Setting, Zeilenzahl im Audit-Log des Aufrufs; siehe Querschnitt), persönlicher Arbeitsvorrat (RAL-QM-HP-1: IHearingAppService.GetWorkQueueAsync liefert Zeilen der Verfahrensübersicht; wessen Sicht es ist, folgt allein aus dem angemeldeten Konto und seinen Rechten — Freigaberecht: genau die eingereichten Verfahren, sonst genau die eigenen laufenden; feste Reihenfolge mit den überfälligen vorn, dahinter maßgebliche Frist und Aktenzeichen, Verfahren ohne Frist am Ende; „überfällig“ dafür als Datenbankabfrage HearingOverdueQuery neben der maßgeblichen Regel HearingOverdueRule; keine Schemaänderung); besitzt RALQMHearings und die RALQMHearing*-Tabellen
Hearings.Blazor interne Prozessseiten, Dashboard, Protokollrecherche /anhoerungen/protokoll (RAL-QM-HP-45), Vorlagen/Postausgang, externe Portaloberflächen; die Kontaktauswahl beim Hinzufügen eines Teilnehmers und in der Nachbeteiligung kennzeichnet den Standardansprechpartner, wählt ihn vor und filtert nach Funktion aus der Werteliste (RAL-QM-HP-9); die Institutionsauswahl beim Hinzufügen bietet nur zuordenbare Institutionen an – aktiv, mit mindestens einem aktiven Ansprechpartner, nicht bereits aktiv beteiligt (RAL-QM-HP-5 AC-2; der Server entscheidet unabhängig davon), die Nachbeteiligung behält ihre Auswahl; im laufenden Verfahren bietet jede aktive Beteiligungszeile „Zugang entziehen“ oder „Zugang wieder freigeben“ mit Bestätigung ohne Begründungsfeld und Fokusrückgabe zum Auslöser über ElementFocus, der Zustand „Zugang entzogen“ steht mit Zeitpunkt als Text in der Zeile (RAL-QM-HP-5 AC-10); die Kachel „Meine offenen Aufgaben“ der Startseite (über HearingPortalWorkspaceOptions registriert) zeigt seit RAL-QM-HP-1 den Arbeitsvorrat des angemeldeten Kontos: acht Zeilen je Seite mit dem CohesionPager (dessen Seitengrößen-Auswahl bewusst aus ist), die Zeile mit den Bausteinen der Verfahrensübersicht (Status- und Überfällig-Abzeichen, Fristchip mit der maßgeblichen Frist, Zuständiger); die Kachel sortiert und siebt nichts selbst; die Teilnahmeansicht des Portals sammelt seit RAL-QM-HP-38 mehrere Dateiauswahlen mit je einem eigenen Feld und sendet sie mit der Stellungnahme, nennt die Frist als Datum nach der UTC-Tagesregel und behält Eingaben nach jeder Abweisung außer der neutralen (siehe Querschnitt 8.5); die Seite Fristvorgaben führt die Anlagen-Höchstzahl in einer eigenen Fläche

RAL-QM-HP-70, RAL-QM-HP-71 ergänzen innerhalb dieses Slices HearingAdditionalRecipient als ausgewählte Kontaktstände der bestehenden Beteiligung und HearingReminderRun als unveränderliche Zielauswahl mit festem internem Berichtsempfänger. Der Postausgangsmanager verzweigt sämtliche sieben Nachrichtenarten auf Primärkontakt, gewählte Zusatzkontakte und deren wirksame Vertretung; die Beteiligung und der operative Portalstand bleiben gemeinsam. IHearingReminderReportAppService liefert den geschützten Bericht, dessen Prozessor/Worker aktuelle Postausgangszustände auswertet und die Zwischen-/Endmeldung versendet. Hearings.Blazor besitzt Mehrfachauswahl und /anhoerungen/{id}/erinnerungen[/{runId}]. Die Tabellen, Snapshots, Claims und ReminderRunId gehören ausschließlich Hearings.

RAL-QM-HP-40 ergänzt am bestehenden HearingResponse die Sequenz, Festschreibung und Auftragsidentität. Der bestehende Kontoportal-Vertrag besitzt Erstabgabe und explizite Ergänzung; beide nutzen die atomare Dateiverarbeitung aus RAL-QM-HP-38. HearingResponseSequence hält die Sortierung und den aktuellen Gesamtstand für Portal, Rücklauf und Abschluss zusammen. Es entsteht kein weiteres Feature. Der alte Änderungsweg wird abgewiesen; der eigene Zustimmungsumfang aus RAL-QM-HP-39 ist über Consents integriert und wird atomar je Abgabe nachgewiesen. ADR-10 dokumentiert den Übergang von der Vorbereitung zum technischen Schreibwechsel.

Die Eingangsbestätigung RAL-QM-HP-35 bleibt im Hearings-Slice: HearingAccountPortalAppService legt beim Speichern einer Abgabe je Empfängerart einen HearingResponseNotification-Auftrag an und hält je Anlage den SHA-256 fest; HearingResponseNotificationProcessor (Worker HearingResponseNotificationWorker) stellt zu und meldet Fehler über den Störungsweg aus RAL-QM-HP-37, dessen Detektor und Wiederholung die Aufträge kennen. Authorization liefert nur die Adressen interner Konten über IStaffNotificationRecipientReader.

Die öffentliche Anzeige RAL-QM-HP-47 bleibt im Hearings-Slice: das Aggregat trägt Kennzeichen und öffentliche Kennung (Hearing.Publication.cs), HearingAppService.PublishAsync/UnpublishAsync schalten sie, IPublicHearingAppService liefert die öffentliche Projektion PublicHearingDto anonym als Liste und dem Integrationsclient als Bestand (PublicHearingFeedDto, voll oder als Delta mit Entfernungen). Der Controller HearingPublicationFeedController und die Bearer-Registrierung HearingPublicationFeedAuthentication gehören dem Hearings-Runtime; der Host komponiert nur. Seit RAL-QM-HP-78 kommen HearingPublicationReceiptController und PublicHearingReceiptAppService (Quittung), der Leser HearingPublicationSyncReader und der Monitor HearingPublicationSyncMonitor mit Worker dazu, alle im Ordner Services/Publication des Hearings-Runtime. Hearings.Blazor besitzt die anonyme Seite /anhoerungen/oeffentlich im Portal-Layout und die Karte „Öffentliche Anzeige“ der Detailseite.

Der Kopierpfad RAL-QM-HP-12 bleibt im Hearings-Slice: IHearingCloneAppService liefert eine lesende Vorschau und erzeugt einen eigenständigen Entwurf aus bestätigten Institutionen und Arbeitsunterlagen. Institutions liefert aktuelle Kontakte über seinen vorhandenen Vertrag; CAF Documents kopiert Descriptor und Bytes über IDocumentFilingPort. Die Herkunft samt akteurgebundener Vorgangskennung liegt am Hearing-Aggregat, der Auftragsfingerabdruck bleibt vom Audit ausgeschlossen. Vorbereitete Kommunikationsschritte aus RAL-QM-HP-24 werden als neue bearbeitbare Kindobjekte übernommen, globale Vorlagen dagegen bei Anlage neu gebunden. Keine neue Projekt- oder Modulkante.

RAL-QM-HP-36 ergänzt die Auswertung der Rückmeldungen ohne weiteres Feature und ohne neues Recht. Hearings.Contracts trägt den Beteiligungsstand (HearingParticipationState: die acht Stände der Rangliste und „Zurückgezogen“), die Hinweise (HearingParticipationHint), die von Ansicht und Export geteilten Filter (GetHearingResponseOverviewInput) und die Herkunft einer Abgabe (HearingResponseOrigin). In der Runtime leitet HearingParticipationStateRule Stand, Hinweise, „offen“ und Filtertreffer aus den gespeicherten Sachverhalten ab; HearingParticipationMails liest Einladung und Zustellergebnis aus den Postausgangszeilen. HearingResponseAppService liefert daraus Übersicht und CSV-Export aus derselben Lesung, eine Zeile je Institution. Hearings.Blazor zeigt die Zeilen über HearingParticipationTable im Reiter „Rücklauf“ der Detailseite und im Block „Beteiligungsstand“ der Rückmeldungsseite /anhoerungen/{id}/rueckmeldungen; Filter und Export gibt es nur dort. Ableitung: Laufzeitsicht 6.2, Rechte: Querschnitt §1, Spalte Origin: Querschnitt §2.

Der formelle Abschluss RAL-QM-HP-51 bleibt ebenfalls im Hearings-Slice. HearingClosureManager bietet eine Zweiphasen-API, die der direkte Abschluss (HearingAppService.CompleteAsync) und der kombinierte einstufige Abschluss (HearingAdjudicationAppService.DispatchSingleStageClosureAsync) gemeinsam nutzen (RAL-QM-HP-34-Lehre: eine Regel, zwei Routen): PrepareClosureAsync vor der Transition (Endfassungsprüfungen, Empfänger mit wirksamem Endfassungs-Grant, Vorprüfung Vorlage 70, Namensvorprüfung, Berichtsmodell, Bericht über den Port), unmittelbar danach im try-Block des Anwendungsdienstes AttachClosureReportAndGrantFinalVersion (Hearing.AttachClosureReport, Endfassungs-Grants) und nach der Transition CompleteClosureAsync (Abschlussmitteilung, Endfassungsbenachrichtigung 70, Verlauf 810/820, Widerruf laufender Artefakte). Der Konstruktor des Managers ist unverändert; IDocumentContentPort, Antworten, Bescheidungen und die Id-Fabrik reichen die Anwendungsdienste als Methodenparameter durch. Die reinen Vorabprüfungen liegen am Aggregat (Hearing.EnsureCompletable, EnsureCompletableSingleStage); die Transitionen rufen sie selbst auf. Die Empfängermenge nach Regel 4 bestimmt HearingFinalVersionRecipients (aktiv, maßgebliche Antwort ist eine Stellungnahme, Zugang nicht entzogen; maßgeblich ist seit RAL-QM-HP-40 der letzte Stand je Teilnahme nach HearingResponseSequence.Order), die wirksame Bescheidung HearingEffectiveReply, die auch die RAL-QM-HP-46-Welle nutzt. Welche Bescheidung eine Beteiligung erhalten hat, steht an der Beteiligung (AdjudicationDispatchedAtUtc, AdjudicationReplyId); HearingPortalRules.DispatchedReplies leitet daraus den übermittelten Stand für Portal und Rückmeldungsübersicht ab. Die Welle (HearingAdjudicationAppService) plant erst (Empfänger nach Regel 4, bei einer Auswahl genau die gewählten, Pflichtprüfungen) und schreibt dann; das Aggregat hält fest, wer bedient wurde, und setzt den Zeitpunkt der vollständigen Übermittlung, sobald keine erforderliche Beteiligung mehr offen ist.

Den Bericht erzeugt HearingClosureReportGenerator hinter IHearingClosureReportGenerator: Modell nach Regel 4, PDF über HearingClosureReportPdfWriter (Fließtext je Beteiligung, keine Tabelle, /Title und /Lang de-DE), Ablage über IDocumentContentPort.CreateAsync nach dem RAL-QM-HP-24-Muster. Begleitbrief (RAL-QM-HP-24) und Bericht teilen den Text-PDF-Kern HearingTextPdfWriter (PDF 1.4, Helvetica/WinAnsi, Umbruch nach gemessener Glyphenbreite, „Seite n von m“, deterministisch, ohne Paket); HearingCoverLetterPdfWriter ist darauf zurückgeführt und erzeugt dieselben Bytes wie vorher. Keine neue Projekt- oder Modulkante: Documents wird nur über die vorhandenen Ports genutzt (CreateAsync, DescribeManyAsync, die Löschreservierung von IDocumentFilingPort für die Kompensation), Authorization nur über Grants und Rollen-Seed, Messaging über Ergänzungsbindung, Begleitbrief-Regel, Vorlage und Seed; keine Migration. Ablauf und Guard-Reihenfolge: Laufzeitsicht.

RAL-QM-HP-41 ergänzt die Kontaktmeldung ohne weiteres Feature und ohne neues Recht. Eine Institution meldet im laufenden Verfahren eine andere Kontaktperson; der Referent übernimmt sie, ersetzt sie durch eine andere Person oder verwirft die Meldung. HearingContactReport ist ein eigenes Aggregat neben dem Verfahren (Tabelle RALQMHearingContactReports), je Beteiligung höchstens eine offene Meldung. Der Kontoportal-Vertrag IHearingAccountPortalAppService trägt Melden und Zurückziehen und liefert mit der Teilnahmeansicht die eigenen Meldungen samt Zustand und die aktive Funktionsliste. Intern liest und entscheidet IHearingContactReportAppService: Liste, Hinweis (nur Institutionsnamen), Entscheidungsgrundlage, Übernehmen, Ersetzen, Verwerfen. Die Übernahme in die Stammdaten ruft den Institutions-Vertrag „einen Kontakt hinzufügen“ (§5.2.2) und den vorhandenen Empfängerwechsel; „nur dieses Verfahren“ schreibt den Empfängerstand ohne Stammdatenkontakt (ADR-14). Die Auswahl zusätzlicher Empfänger verlangt deshalb keinen Kontaktbezug des Hauptempfängers mehr. Hearings.Blazor zeigt im Portal die Karte „Kontaktperson melden“ der Teilnahmeansicht (PortalContactReportPanel), in der Verfahrensakte die Kennzeichnung „Kontaktmeldung offen“ an der Beteiligung mit dem Panel HearingContactReportPanel (Gegenüberstellung, Entscheidungen, interne Notiz) und den Hinweis HearingOpenContactReportNotice vor Bescheidübermittlung, kombiniertem Abschluss, Abschließen und Abbrechen. Ablauf: Laufzeitsicht 6.2, Rechte: Querschnitt §1, Persistenz: Querschnitt §2.

5.2.4 ValueLists

Teilprojekt Verantwortung
ValueLists.Contracts ValueListKind, Eintrags-/Options-DTOs, IValueListLookup (In-Process-Lesemodell für fremde Feature-Runtimes), die beiden AppService-Interfaces, die drei Permissions, Fehlercodes, die Seed-Ids, Localization, Menüname
ValueLists (Runtime) vier Aggregate über der gemeinsamen Basisklasse ValueListEntry, Standardfristen als Owned Value Object der Verfahrensart, Pflegedienst ohne Löschoperation, cachefreier Lookup, EF-Mapping und Seed-Katalog (seit RAL-QM-HP-119 auch der Kontaktfunktionen-Katalog); besitzt RALQMMarkTypes, RALQMProcedureTypes, RALQMCountries, RALQMContactFunctions
ValueLists.Blazor Pflegeseite /administration/wertelisten (Reiter je Liste links, Formular rechts), Menüeintrag „Wertelisten“ in der Gruppe Administration

Der Slice ersetzt seit RAL-QM-HP-4 vier zuvor fest verdrahtete Ausprägungsmengen — Kennzeichnungsart und Verfahrensart waren C#-Enums, Land und Funktion eines Ansprechpartners Freitextfelder — durch Katalogtabellen, die ein Systemadministrator über die Oberfläche pflegt. Begründung und Preis stehen in ADR-07.

Eine Tabelle je Liste, ein Aggregat je Tabelle. MarkType, ProcedureType, Country und ContactFunction erben Code, Namen, Aktivkennzeichen und Sortierung von ValueListEntry; die Basisklasse selbst ist im Modell ausdrücklich Ignored und damit keine Tabelle. Das ist bewusst keine gemeinsame Tabelle mit Diskriminatorspalte: Nur die Verfahrensart trägt zusätzliche Spalten (die drei Standardfristen), die in einer Sammeltabelle für drei Viertel der Zeilen leer stünden, und die Eindeutigkeit des Code bleibt so ein schlichter Unique-Index je Tabelle — gefiltert auf [IsDeleted] = 0, damit eine soft-gelöschte Zeile ihren Code nicht dauerhaft belegt. ValueListKind ist ein reiner Contract-Enum, der eine Liste adressiert, nie einen Wert in ihr, und wird nirgends persistiert: Ein Enum, der wieder Ausprägungen aufzählte, wäre der Rückweg in genau das, was diese Story abgeschafft hat.

Die Abhängigkeit läuft nur in eine Richtung. ValueLists.Contracts referenziert ausschließlich Authorization.Contracts — für den Namen der Permission-Gruppe RALQM — und kennt weder Hearings noch Institutions. Das ist keine Sparsamkeit, sondern Voraussetzung: Die konsumierenden Slices (Hearings, Institutions und, für die Rollen-Grants, Authorization) hängen ihrerseits an ValueLists.Contracts, jede Gegenrichtung wäre ein Projektzyklus. Deshalb gibt es für das Lesen der Auswahloptionen auch kein hearings- oder institutionseigenes Recht, sondern das eigenständige RALQM.ValueLists.Read (siehe Querschnittliche Konzepte §1).

Fremde Slices sehen nur Ids und Anzeigenamen. Hearings speichert MarkTypeId und ProcedureTypeId (beide Pflicht), Institutions CountryId und je Kontakt FunctionId (beide optional) — Guid-Spalten ohne Foreign Key, wie es die Modulgrenze für jede modulübergreifende Referenz verlangt. Aufgelöst wird über IValueListLookup — in den *.Blazor-Projekten über den permissiongeschützten IValueListLookupAppService daneben. Die Auflösung ist nach Richtung verschieden streng, und diese Unterscheidung liegt je Slice an genau einer Stelle (HearingValueListNameMap, InstitutionValueListNameMap):

Richtung Verhalten bei unbekannter oder deaktivierter Id
Schreiben (Anlegen/Bearbeiten) Fachfehler im Anwendungsdienst, nichts wird gespeichert
Lesen (Liste, Detail, CSV) Platzhalter — und strukturierte Warnung; eine kaputte Zeile darf keine ganze Seite fällen
Ausgehender Versand (Hearings) Fachfehler InvalidProcedureTypeReference statt Platzhalter — ein Anschreiben mit einem Gedankenstrich an der Stelle der Verfahrensart verlässt das Haus nicht

Beide Karten lösen eine ganze Seite mit einem Aufruf je Liste auf, nicht mit einem je Zeile.

Kein Cache. Der Lookup liest bei jedem Aufruf über die Repositories; eine neu angelegte, umbenannte oder reaktivierte Ausprägung ist ohne Neustart und ohne Invalidierungspfad sichtbar. Fällt das später messbar ins Gewicht, ist ein Cache mit Invalidierung beim Schreibzugriff eine lokale Optimierung dieses Slices und ändert an keiner hier beschriebenen Grenze etwas.

ValueLists.Blazor referenziert CAF.Modules.UI.Blazor — anders als die drei anderen fachlichen Feature-*.Blazor, aber wie Settings.Blazor — nicht direkt: Die Pflegeseite lädt je Reiter eine vollständige Liste und braucht deshalb weder Grid-Datenadapter noch Toolbar-Preset aus der Grid-Naht. Über den produktweiten UI-Baustein kommt das Modul ohnehin transitiv; eine direkte Referenz würde eine Abhängigkeit behaupten, die es nicht gibt.

Der Kontaktfunktionen-Katalog wird seit RAL-QM-HP-119 gesät. RAL-QM-HP-4 hatte diese Liste bewusst leer ausgeliefert, weil Kontaktfunktionen Kundendaten sind und ein Seeder keine Fachbegriffe erfinden darf. Der Katalog aus 15 Rollen in neutraler, männlicher und weiblicher Form (44 Codes, deutsche und englische Namen) ist aus dem Altexport des Anhörungsportals abgeleitet und vom PO bestätigt, und der Institutionsimport weist Kontakte mit unbekannter Funktion ab — der Katalog muss also auf jeder Umgebung vor dem ersten Import stehen. ValueListsDataSeedContributor trägt ihn wie die drei anderen Listen idempotent je Code nach und überschreibt nie, was ein Administrator gepflegt hat; zusätzlich legt er keinen Eintrag an, dessen deutscher Name schon unter einem anderen Code existiert (kein Namenszwilling für Import und Auswahlfelder). Die Ids sind je Katalogordinal fest (a4000000-…); ein Code darf nie wie der Name eines anderen Eintrags lauten, weil der Lookup Codes und Namen in einem Durchgang vergleicht. Der Demo-Seed behält seine eigenen DEMO-FUNKTION--Codes daneben. Eine Migration gibt es nicht; der Seeder läuft mit dem Migrator.

5.2.5 Filing

Der Slice besitzt die verfahrensunabhängige Ablage aus RAL-QM-HP-19: einen gemeinsamen Bereich und je berechtigtem internem Konto einen persönlichen Bereich (FilingAreaKind), darin Ordner und Dateien (FilingEntryKind) bis zu 16 Ebenen tief, dahinter den zweistufigen Papierkorb (FilingEntryState: Active, Trashed, Purging, Purged).

Teilprojekt Verantwortung
Filing.Contracts Bereichs-, Ordner-, Element-, Upload- und Papierkorb-DTOs, seit RAL-QM-HP-16 Übernahme- und Verwendungs-DTOs, IFilingAppService und IFilingAdministrationAppService, die Permissions RALQM.Filing mit den Kindern .Read, .Manage, .Purge sowie das eigenständig vergebbare RALQM.Filing.Transfer, Fehlercodes RALQM.Filing:…, Localization, Menüname
Filing (Runtime) Aggregate FilingArea, FilingEntry und (RAL-QM-HP-16) FilingHearingUsage samt Fachhistorie FilingHistoryEntry; Namens-, Hierarchie- und Tiefenpolitik, Bereichsrevision als Sperre jeder Mutation, Papierkorb mit FilingPurgeProcessor/FilingPurgeWorker, administrative Bereichsübertragung, Übernahme ins Verfahren und Verwendungsnachweis; EF-Mapping; besitzt RALQMFilingAreas, RALQMFilingEntries, RALQMFilingHistory, RALQMFilingHearingUsages
Filing.Blazor Explorer /filing über den separat paketierten CohesionDataBrowser, gemeinsame Papierkorbansicht /filing/trash (Ablage- und Verfahrenszeilen), administrative Bereichsübertragung /administration/ablage-uebertragen, seit RAL-QM-HP-16 die Verwendungsansicht je Datei und die Löschbestätigungen mit Verfahrensliste

Der Slice besitzt keine Dateiinhalte. Metadaten und Bytes liegen wie überall sonst bei ABP File Management hinter dem neutralen IDocumentFilingPort von CAF.Modules.Documents.Contracts; Filing speichert Bereichseigentum, Elementzuordnung, Papierkorbzustand, Fachhistorie und — seit RAL-QM-HP-16 — den Verwendungsnachweis. Die persönlichen Bereiche fremder Konten bleiben bei jeder Operation verborgen, auch für Administratoren (siehe Laufzeitsicht §6.5).

Filing.Contracts referenziert ausschließlich Authorization.Contracts — auch dort, wo der Slice mit Hearings zusammenarbeitet: Der Vertrag bleibt schmal, die Verzahnung liegt in der Runtime. Verfahrensfakten führt er wie schon FilingTrashItemDto als blanke Guid/string; HearingDocumentPurpose, HearingDocumentRole und HearingStatus liegen als zahlengleiche Filing-Enums gespiegelt daneben (FilingHearingDocumentPurpose, FilingHearingDocumentRole, FilingHearingStatus). Die Zahlenwerte sind Draht- und Persistenzvertrag und dürfen zwischen Original und Spiegel nicht auseinanderlaufen.

Die fachliche Ablage-Verfahrensintegration wird durch Filing orchestriert. Filing (Runtime) referenziert Hearings.Contracts. Seit RAL-QM-HP-65 konsumiert Hearings (Runtime) zusätzlich ausschließlich die prozesslokalen Bestands-/Recovery- und Ausführungspolicyverträge aus Filing.Contracts (ADR-21). Diese Gegenkante erlaubt weder Filing-Entities noch Repositories, Dateizugriff oder Filing-Anwendungsfälle. Beide verzahnten Abläufe orchestriert deshalb Filing und holt sich die Verfahrenshälfte über einen prozesslokalen Hearings-Port mit [RemoteService(false)]: IHearingDocumentTrashPort für entfernte Verfahrensdateien und die Kopie in die Ablage, IHearingDocumentAdoptionPort (RAL-QM-HP-16) für die Übernahme in der Gegenrichtung. Für den Verwendungsnachweis gibt es weiterhin keinen Usage-Provider nach dem Muster von IInstitutionUsageProvider: Die neue Gegenkante ist ausschließlich für den produktweiten Schreib-/Löschschutz und die zentrale Fristpolicy bestimmt. Verwendungszeilen sind Filing-Daten; von Hearings kommen nur die autorisierten Fakten zu bekannten Verfahrensdokumenten.

Umgekehrt gibt es genau eine UI-Kante. Der Bedien-Einstieg „Aus der Ablage übernehmen“ liegt in der Verfahrensakte, deshalb referenziert Hearings.Blazor seit RAL-QM-HP-16 Filing.Contracts und konsumiert IFilingAppService. Das ist eine Feature-Blazor-→-fremde-Contracts-Kante, dieselbe, die jede andere Feature-UI bereits trägt; die enge Hälfte Hearings.Blazor → Filing (Runtime) bleibt verboten und ist in RepositoryArchitectureTests als roter Gegenfall neben der erlaubten Kante mitgeführt. Es wurde dafür keine Regel gelockert.

5.2.6 Abhängigkeitsrichtung der Feature-Slices

Consumer Erlaubte eigene Projektabhängigkeiten
ValueLists Contracts Authorization Contracts
Institutions Contracts Authorization Contracts
Hearings Contracts Authorization Contracts, Documents Contracts
Filing Contracts Authorization Contracts
Consents Contracts Authorization Contracts
ValueLists Runtime ValueLists Contracts
Consents Runtime Consents-, Authorization-Contracts (IInstitutionAccountAccessReader.GetActivationCandidateAsync für die Aktivierungszustimmung, 5.2.7)
Institutions Runtime Institutions-, ValueLists-Contracts
Authorization Runtime Authorization-, Institutions-, Hearings-, Filing-, ValueLists-, Consents- (Aktivierungsnachweis als Zugangsbedingung, RAL-QM-HP-39) und Documents-Contracts
Hearings Runtime Hearings-, Institutions-, ValueLists-, TextTemplates-, Consents- (Zustimmungsnachweis im Abgabeabschluss, RAL-QM-HP-39), Filing- (nur Bestands-/Recovery-Schutz und zentrale Fristpolicy, ADR-21) und Documents-Contracts; ABP AuditLogging Domain (IAuditLogRepository — der Verfahrensverlauf liest die Entity History des Verfahrens als Store-Lesung hinter der Hearings-Berechtigung, nicht über den berechtigungsgeschützten IAuditLogsAppService; RAL-QM-HP-7, Review zu !79)
TextTemplates Runtime eigene Contracts; nativer ABP-TextTemplateManagement-Application-Pfad
Filing Runtime Filing-, Authorization-, Hearings- und Documents-Contracts (die Verzahnung mit Hearings liegt in der Runtime, nicht im Vertrag; siehe 5.2.5)
Produkt UI.Blazor Produkt-Contracts, Hearings-Contracts (Statusenums), CAF.Modules.UI.Blazor
Produkt-Shell Produkt-Contracts, Authorization-/Institutions-/Gdpr-/Hearings-Contracts, Produkt UI.Blazor
ValueLists Blazor ValueLists-, Authorization-Contracts, Produkt-Shell-Contracts, Produkt UI.Blazor (CAF.Modules.UI.Blazor nur transitiv, siehe 5.2.4)
Institutions Blazor Institutions-, Authorization-, ValueLists-Contracts, Produkt-Shell-Contracts, Produkt UI.Blazor, CAF.Modules.UI.Blazor
Authorization Blazor Authorization-Contracts, Produkt-Shell-Contracts, Produkt UI.Blazor, CAF.Modules.UI.Blazor
Hearings Blazor Hearings-, Institutions-, ValueLists-, Filing-Contracts (RAL-QM-HP-16, Einstieg „Aus der Ablage übernehmen“), Consents-Contracts (RAL-QM-HP-39: Zustimmungsseite im Portal, Bestätigung im Abgabeformular, Nachweisspalte), Produkt-Shell-Contracts, Produkt UI.Blazor, CAF.Modules.UI.Blazor
Filing Blazor Filing-, Authorization-, Documents-Contracts, Produkt-Shell-Contracts, Produkt UI.Blazor, CAF.Modules.UI.Blazor
Consents Blazor Consents-, Authorization-Contracts, Produkt-Shell-Contracts, Produkt UI.Blazor
ABP-Ersatz-Features Blazor (Identity, Languages, TextTemplates, AuditLogs, Gdpr, Settings) eigene Contracts, Produkt-Shell-Contracts, Produkt UI.Blazor, CAF.Modules.UI.Blazor (Settings nur transitiv)
Host Client, Framework Authentication/Documents/UI, alle Feature Runtime+Blazor

Die Tabelle nennt Projektabhängigkeiten. Jedes *.Blazor-Projekt referenziert zusätzlich das Cohesion-Paket direkt — Begründung in 5.1.3. Die Kante Feature-Blazor → Produkt UI.Blazor ist die einzige erlaubte Kante zwischen UI-Assemblies; Feature-Blazor → Feature-Blazor und Shell → Feature-Blazor bleiben verboten (Regel ClientToFeatureRuntime).

Immer verboten: Contracts → Runtime/EF/Host, Feature-Blazor → Feature-Runtime oder eine fremde Runtime, GZ-Runtime → fremde GZ-Runtime oder deren Entities/Repositories/Tabellen, sowie jedes Modul → ausführbarer Host. Über Modulgrenzen (auch zwischen Features) werden nur IDs gespeichert, keine Foreign Keys — siehe Datenmodell in der Verteilungssicht und Persistenzregeln in Querschnittliche Konzepte §2.

Diese Trennung ist eine Compile-Time-Eigenschaft, durch Architekturtests abgesichert, keine bloße Ordnerkonvention. Die beiden Regeln FeatureRuntimeToHostOrClient und FeatureRuntimeToForeignRuntime leiten die Feature-Zugehörigkeit aus dem Projektnamen ab statt aus einer gepflegten Liste; Every_feature_runtime_project_on_disk_is_guarded_in_both_directions liest Features/*/Runtime vom Dateisystem und verlangt für jedes geordnete Paar einen Verstoß. Die sechste fachliche Feature-Runtime, Consents (5.2.7, RAL-QM-HP-39), war damit ab dem Anlegen ihrer Projektdatei abgesichert und nicht erst, wenn jemand eine Regel nachträgt.

5.2.7 Consents

Seit RAL-QM-HP-39 (ADR-11). Der Slice ist nach der Abhängigkeitstabelle nummeriert, damit deren verlinkter Anker stabil bleibt.

Teilprojekt Verantwortung
Consents.Contracts ConsentDocumentKind (Nutzungsbedingungen, Datenschutzhinweise), Fassungs-, Bestätigungs- und Nachweis-DTOs, die AppService-Interfaces für Pflege, Lesen der geltenden Fassungen und Aktivierung, die In-Process-Ports IConsentProofRecorder (Hearings) und IConsentProofReader (Authorization, Hearings), die Rechte RALQM.Consents.Documents und .Publish, Fehlercodes, die gesamte Lokalisierung der Zustimmung (Ressource RALQMConsents)
Consents (Runtime) Fassungskatalog RALQMConsentDocumentVersions (nur Einfügen; Auflösung: höchste laufende Nummer je Art mit „gültig ab ≤ T“), Zustimmungsnachweise RALQMConsentProofs (nur Einfügen, je Konto ein Aktivierungsnachweis, je Abgabe ein Nachweis), Schutz gegen Ändern und Löschen beider Tabellen, Veröffentlichung in serialisierbarer Transaktion, Aktivierungsdienst, Demo-Fassungen nur unter RALQM:SeedDemoData
Consents.Blazor Pflegeseite /administration/rechtstexte für den Systemadministrator, Menüeintrag „Rechtstexte“ in der Gruppe Administration

Die Zustimmungsseite /portal/zustimmung liegt im Portal (Hearings Blazor), weil das Portallayout dort liegt und Feature-Blazor → Feature-Blazor verboten ist. Den Bestätigungsbaustein mit zwei Kästchen und Lesedialog teilen sich Zustimmungsseite und Abgabeformular über Produkt UI.Blazor (ConsentDocumentConfirmation); er erhält Texte als Parameter und ruft keinen Dienst. Der Nachweis einer Abgabe entsteht im Abgabeabschluss von Hearings über IConsentProofRecorder in der umgebenden Unit of Work und damit in derselben Datenbanktransaktion (gemeinsamer HearingPortalDbContext). Über die Modulgrenze werden nur Kennungen gespeichert (UserId, HearingResponseId), keine Foreign Keys.

5.3 Direkt verwendete ABP-Bausteine

Ergänzend zu den eigenen Modulen nutzt die Anwendung ABP-Vertikalen als Implementierungssubstrat, ohne sie zu eigenen CAF-Modulen zu machen: Account Public/Admin und Identity Pro (Login, lokale Benutzer/Rollen), Permission Management, Setting Management, Audit Logging, Text Template Management, Language Management/GDPR, File Management (Domain/EF/Application; keine generische UI/API) sowie BLOB Storing Database und Emailing/Background Workers. Von diesen Vertikalen wird jeweils nur die Serverhälfte komponiert (Application/HttpApi/EF); ihre Blazor-Oberflächen sind durch Produktseiten ersetzt und die zugehörigen Pakete entfernt. Vom LeptonX-Theme bleibt allein die MVC-Hälfte, die die /Account/**-Razor-Pages trägt.

5.4 Zusammenspiel im Request

Wie ein Blazor-Seitenaufruf durch Contract, Application Service, Aggregate und Persistenz läuft, beschreibt die Laufzeitsicht. Betriebs- und Deployment-Aspekte (ein Deployable, eine Datenbank, lokale Infrastruktur) stehen in der Verteilungssicht.

Weiterführend: Entwicklungsleitfaden, Getting Started, Konventionen.

06. Laufzeitsicht

Dieses Kapitel zeigt das dynamische Verhalten der in der Bausteinsicht beschriebenen Module anhand der wichtigsten Laufzeitszenarien: Anmeldung, Anhörungs-Lebenszyklus, externe Kontoprovisionierung, Dokumentfreigabe, Arbeitsablage mit Papierkorb und die Übernahme einer Ablagedatei ins Verfahren. Alle Abläufe sind im aktuellen Stand implementiert — mit den in Risiken und technische Schulden benannten offenen Nachweisen und der durch RAL-QM-HP-31 umgestellten Institutionsbindung. Details zu Modulgrenzen und Ownership stehen in der Bausteinsicht und den Querschnittlichen Konzepten, verbindliche Bauregeln im Entwicklungsleitfaden.

6.1 Login und Authentifizierung

Die Produktshell baut Menüs allein über Routerparameter auf (RAL-QM-HP-73). Initialisierung und Menüabfragen werden serialisiert. IAsyncDisposable beendet die eigene Lebensdauer und wartet bereits laufende Arbeit ab, bevor der Circuit-Scope freigegeben wird; danach startet keine weitere Abfrage. ICancellationTokenProvider.Use(CancellationToken.None) entkoppelt diese renderer-eigene Arbeit vom kurzlebigen HTTP-RequestAborted. Der Abbau wartet sie unter den bestehenden Provider-Timeouts ab. Cancellation-, Scope- und andere unerwartete Fehler werden nicht abgefangen oder unterdrückt.

Keycloak ist Token-Issuer und Identitätsquelle; CAF.Modules.Authentication (modules/Authentication/src) ist der anwendungsneutrale OIDC-Client. Er registriert Cookie- und OIDC-Authentifizierung (CAFAuthenticationServiceCollectionExtensions), setzt eine Cookie-Session- Policy (CAFApplicationCookieSessionPolicy, CAFApplicationCookieTokenPropagation) und hält die Session über einen revalidierenden AuthenticationStateProvider (CAFRevalidatingAuthenticationStateProvider, CAFSecurityStampRevalidationPolicy) periodisch aktuell.

sequenceDiagram
    actor U as Benutzer
    box transparent RALQM-Host
        participant App as Blazor Web App (Shell)
        participant Auth as CAF.Modules.Authentication
        participant Id as ABP Identity (lokal)
    end
    participant Kc as Keycloak

    U->>App: Aufruf einer geschützten Seite
    App->>Auth: Challenge auslösen
    Auth->>Kc: Redirect Authorization Code
+ PKCE (code_challenge) U->>Kc: Anmeldung (Credentials/2FA) Kc-->>Auth: Redirect mit Code Auth->>Kc: Code-Tausch inkl. code_verifier Kc-->>Auth: ID-/Access-/Refresh-Token Auth->>Auth: Pflicht-Claims prüfen (u. a. sub) Auth->>Id: IdentityUser über sub
finden/verknüpfen Id-->>Auth: GZ-Rolle(n) und Grants Auth-->>App: Cookie-Session gesetzt App-->>U: Zielseite mit gewährten Permissions

Wesentliche Punkte:

  1. Die Verknüpfung zwischen Keycloak-Identität und lokalem IdentityUser verwendet ausschließlich den stabilen OIDC-Subject-Identifier sub, niemals die E-Mail-Adresse.
  2. Rollen (RALQMAdministrator, RALQMCaseOfficer, RALQMManagement) sind lokale ABP-Grants; sie werden aus den Feature-Contracts komponiert (siehe Authorization und Autorisierung).
  3. Die Cookie-Session wird nicht nur beim Login geprüft: CAFRevalidatingAuthenticationStateProvider und die Security-Stamp-Policy erzwingen eine periodische Neuprüfung, sodass eine serverseitig gesperrte oder geänderte Identität in laufenden Circuits zeitnah wirkt.
  4. Abmeldung läuft über CAFAuthenticationSignOut lokal und bei Keycloak; Multi-Tenancy ist deaktiviert, Keycloak-Gruppen ersetzen keine GZ-Rolle oder ABP-Permission.
  5. Jeder nachfolgende Application-Service-Aufruf prüft serverseitig erneut seine Permission — die Anmeldung allein gewährt keinen Fachzugriff.

Organisationsanmeldung und Ausfallweg (RAL-QM-HP-63, ADR-15)

sequenceDiagram
    actor M as Mitarbeitende
    participant P as Portal (/login)
    participant K as Keycloak (Realm ral)
    participant O as Organisationsdienst
    M->>P: Mit Organisationskonto anmelden
    P->>K: /Account/Login?anmeldeweg=organisation → authorize mit kc_idp_hint=organisation
    K->>O: Brokering (PKCE)
    O-->>K: Code / ID-Token des Organisationskontos
    K->>K: Federated Identity vorhanden? sonst First-Broker-Login „Deny Access“
    K-->>P: Code (Subject des bestehenden Keycloak-Benutzers, Claim identity_provider)
    P->>P: Sicherheitsprotokoll „LoginPath/SignedInViaOrganisation“, ABP-Anmeldung über sub

Hat das Konto einen zweiten Faktor eingerichtet (RAL-QM-HP-11, ADR-19), fragt Keycloak nach dem Brokering im Post-Broker-Login-Ablauf ralqm-organisation-post-login-otp den Code ab, bevor es den Code an das Portal ausgibt; auf dem Passwortweg tut das der Standard-Browserablauf.

Ist der Organisationsdienst dreimal in Folge nicht erreichbar oder der Ausfallweg erzwungen, blendet die Anmeldeseite den Organisationsknopf aus und verweist neutral auf die Portal-Zugangsdaten; der Ablauf ist dann die bekannte Anmeldung aus 6.1.

6.2 Anhörungs-Lebenszyklus

Die fachliche Prozesssicht KP-HP-01 ergänzt diese technische Laufzeitsicht um Verantwortung, Freigaben, Abschlussvarianten und den getrennten Versandablauf. Der lokale Prozessreport und die interne Dokumentationsvorschau öffnen daraus denselben bpmn-js-Viewer. Scope, Quellstand und fachliche Abnahme stehen auf der Begleitseite. Der Prozesskatalog führt zu den ergänzenden Sichten für Beteiligung, Rückmeldungen, Unterlagen, Kommunikation sowie Konten und Zuständigkeiten.

Der Hauptstatus einer Anhörung (RALQMHearings, Feature Hearings) ist:

stateDiagram-v2
    direction TB
    [*] --> Draft: anlegen
    Draft --> PendingApproval: zur Freigabe einreichen
    PendingApproval --> Approved: freigeben (andere Person)
    Approved --> Dispatched: Versand über Outbox
    Dispatched --> Adjudication: zweistufige Variante
    Adjudication --> Completed: Verfahren abschließen
    Dispatched --> Completed: einstufig (US-33)
    Completed --> [*]
    note right of PendingApproval
        benannte Rücksprünge und Abbruch
        nach Fachregel (nicht dargestellt)
    end note

Die Wahl der Abschlussvariante (US-33) entscheidet, ob Adjudication als eigener Status durchlaufen wird — siehe Punkt 7 unten und Einführung und Ziele.

sequenceDiagram
    actor R as Referent (RALQMCaseOfficer)
    actor M as Geschäftsführung (RALQMManagement)
    box transparent RALQM-Host
        participant H as Hearings AppService
        participant Out as Hearing-Outbox
        participant W as ABP Background Worker
    end

    R->>H: Anhörung anlegen (Draft)
    R->>H: Teilnehmer zuordnen (InstitutionId
aus Institutions.Contracts) R->>H: Dokumente zuordnen (DocumentId
aus Documents.Contracts) R->>H: Zur Freigabe einreichen (PendingApproval) M->>H: Freigeben (andere Person als Einreichung,
Composition Hash sperrt Stand) H->>Out: Versand enqueuen
(RALQMHearingOutboxMessages) Out->>Out: Sendeversuche mit Retry und
Idempotency Key (RALQMHearingSendAttempts) Note over H: Status Dispatched —
Adjudication-Fenster: nur Endfassungen neu/ersetzt W->>H: Fristen als Due Actions verarbeiten
(RALQMHearingDueActions) H->>H: Rückmeldungen der Teilnehmer
(RALQMHearingResponses, ...Replies) R->>H: Bescheidung versenden (Fenster
schließt, Komposition fixiert) R->>H: Abschließen (Endfassung freigeben,
Rückmeldungsübersicht erzeugen) Note over H: Status Completed
  1. Anlegen: Draft entsteht über den owning Application Service; jede Statustransition ist eine benannte Aggregate-Operation (kein SetStatus). Die Anlage als Kopie (RAL-QM-HP-12) liest die Quelle in jedem Hauptstatus ohne Mutation und zeigt aktuelle Kontakte, Pflichtpartner, frische Fristvorschläge und kopierbare Arbeitsunterlagen. Bestätigung bindet Quell- und Vorschauzustand; geänderte Stammdaten verlangen eine neue Vorschau. Ausgewählte Dateien erhalten eigene Bytes und Kennungen; Zuordnungen und Kommunikationsabweichungen zeigen nur auf neue ausgewählte Beteiligungen. Alle Schritte samt Herkunft und Zielhistorie bilden eine Serializable-Transaktion. Dieselbe Vorgangskennung mit demselben Akteur/Auftrag liefert nach erneuter Autorisierung das bestehende Ziel. Freigaben, Antworten, Zugangsdaten, alte Fristen und Versandgeschichte werden nicht kopiert.
  2. Teilnehmer/Institution: Hearings referenziert Institutionen nur über die InstitutionId aus Institutions.Contracts — keine Cross-Module-Navigation oder -Foreign-Key. Zuordnen, Zurückziehen und Wiederaufnehmen einer Beteiligung gibt es seit RAL-QM-HP-5 nur im Entwurf, auch wieder nach Rückzug der Einreichung oder Ablehnung. Ab der Einreichung weist das Aggregat das Zurückziehen mit HearingNotEditable ab, nach dem Versand – in Dispatched und Adjudication – mit dem eigenen Code WithdrawalNotAllowedAfterDispatch, dessen Meldung den vorgesehenen Weg nennt: den Zugangsentzug je Beteiligung (siehe §6.4). Das frühere Zurückziehen nach dem Versand ist ersatzlos entfallen; „betrifft uns nicht“ ist eine Rückmeldung der Institution oder eine interne Erfassung, kein Zurückziehen. Früher nach dem Versand zurückgezogene Beteiligungen bleiben zurückgezogen.
  3. Freigabe: Der Wechsel nach Approved erzwingt eine andere handelnde Person als die Einreichung und sperrt die geprüfte Dokumentzusammensetzung über einen Composition Hash. Seit RAL-QM-HP-25 schreibt jede Freigabe und jede Ablehnung in derselben Unit of Work einen dauerhaften Benachrichtigungsauftrag (RALQMHearingApprovalDecisionNotifications, genau einer je Entscheidungsdatensatz) an den in diesem Moment zuständigen Referenten; ein späterer Zuständigkeitswechsel adressiert ihn nicht um. Ein periodischer Worker stellt ihn wie die Störungsbenachrichtigung zu (Claim je Versuch, Wiederholung nach Frist, Begründung aus dem Entscheidungsdatensatz); Zustellfehler und ein fehlender oder inaktiver Zuständiger werden als Störung sichtbar und nie an andere Konten verteilt. Die Prüfansicht der Geschäftsführung nennt die Zuständigkeit, kennzeichnet zugeordnete Unterlagen ohne abrufbaren Inhalt als Warnung und benennt die noch fehlende Bindung der Anschreiben-Endfassung (RAL-QM-HP-24); sie ändert keine Daten. Seit RAL-QM-HP-22 verlangt die Einreichung aktuelle Anhörungsunterlagen (Zweck HearingMaterial, keine überholte Version) mit Grant je Beteiligung, den gebundenen Einladungstext aus RAL-QM-HP-26 und zustellfähige Empfänger: die aktuelle Institution und der gewählte Kontakt jeder aktiven Beteiligung werden in einer Lesung über Institutions.Contracts geprüft (HearingRecipientValidator), ein anderer aktiver Kontakt heilt nichts, und jede Abweisung nennt die Institutionen, ohne etwas zu schreiben. Einreichung und Rückzug ordnen denselben Auftrag mit Zielgruppe Geschäftsführung: die aktiven Konten der Rolle RALQMManagement werden bei Zustellung über Authorization.Contracts aufgelöst und je einmal beliefert; ohne solches Konto entsteht eine Störung, keine Ersatzverteilung. Dieselbe Empfängerprüfung läuft vor dem Erstversand auf dem freigegebenen Stand und weist die ganze Welle ab, bevor ein Zugangsdatum oder eine Postausgangszeile entsteht. Seit RAL-QM-HP-6 ist die bescheinigte Adresse die operative der Beteiligung: Prüfung, Erstversand, Erinnerungen, Bescheid, Abschluss- und Abbruchmitteilung lesen denselben Empfängerstand aus den Snapshot-Spalten; eine Stammdatenänderung wird als Abweichung gezeigt und nur auf ausdrücklichen Befehl des Referenten übernommen (Übernahme, Empfängerwechsel, Adresskorrektur; nur in Draft, Dispatched, Adjudication). Jede Postausgangszeile friert den verwendeten Stand ein, die Vertretungskopie denselben; die Verfahrensakte zeigt je Beteiligung Stand, Abweichung und die Stände der Aussendungen. Das Portal zeigt den operativen Stand der Beteiligung, einschließlich einer ausdrücklich bestätigten Übernahme; historische Aussendungen bleiben unverändert (ADR-08). Seit RAL-QM-HP-24 ist der Wortlaut jeder Aussendung der Kommunikationsschritt des Zwecks: eine einmalige Kopie des gebundenen Vorlagentexts (beim ersten Öffnen durch den Referenten oder automatisch vor der ersten Zeile), je Institution mit eigener Abweichung, gesperrt für das erste Anschreiben ab der Einreichung und für jeden Zweck ab der ersten Postausgangszeile. Referent und Geschäftsführung prüfen dieselbe Endfassung je gewähltem Empfänger (Briefkopf, Anschrift, Ansprechperson, Betreff, Datum, Wortlaut, Zeichnung, Anhang) als reine Lesung; der Erstversand verlangt den Stempel dieser Prüfung, wenn die Versandseite ihn mitgibt. Jede Teilnehmerzeile entsteht mit ihrem Begleitdokument (PDF über CAF Documents, Absender dieses Moments, Zeichnung nach Zweck) und geht als Nachricht mit Anhang hinaus; ohne vollständigen Absender entsteht weder Dokument noch Zeile. Jede Aussendung trägt genau einen tokenfreien Portal-Link. Die Zeile hält den versendeten Wortlaut fest; eine vertrauenswürdig zugeordnete Unzustellbarkeit setzt das aktuelle Ergebnis des Empfängers, ohne die Übergabe umzuschreiben.
  4. Versand: Kein "Best effort" im Request — eine persistente Hearing-Outbox mit Sendeversuchen, Retry und Idempotency Keys entkoppelt Versand von der HTTP-Antwort. Seit RAL-QM-HP-23 meldet die Versandseite den Erfolg des Erstversands als „Befragung eingeleitet“ und den Zeilenstatus als „an Mailtransport übergeben“ - beides bedeutet nicht „zugestellt“: Dispatched, DispatchedAtUtc, der Verlaufseintrag, die Hauptaufträge aller aktiven Beteiligungen (plus Vertretungskopie aus RAL-QM-HP-9) und die Fristaktionen werden in einer Transaktion festgeschrieben; scheitert ein Schritt vor dem Commit, bleibt nichts davon sichtbar, ein paralleler oder wiederholter Aufruf erzeugt keine zweite Welle. SentAtUtc einer Postausgangszeile dokumentiert die Übergabe an den Mailtransport, keinen Eingang im Postfach; die Statusansicht je Empfänger (GetDeliveryStatusAsync, Recht .Dispatch) benennt sie so und zeigt je Zeile die Sendeversuche in ihrer Reihenfolge mit Ergebnis und sicherem Fehlercode – ein Retry (.Outbox.Manage, nur für nicht übergebene Zeilen) fügt einen Versuch mit demselben Wortlaut hinzu und überschreibt keinen. Ein Transportfehler ändert weder Status noch Einleitungszeit. Eine korrigierte Neuzustellung ist ein eigener .Dispatch-Befehl mit Verfahrens- und Nachrichtenstempel sowie akteurgebundener Operation-ID. Sie friert die korrigierte Adresse in einem neuen Auftrag ein; der alte bleibt mit Wortlaut, Empfänger und Versuchen als Superseded erhalten. Unique-Indizes verhindern zweite Nachfolger. Erst die erfolgreiche SMTP-Übergabe des Nachfolgers erledigt die alte Störung, ohne ihr einen erfundenen SentAtUtc zu geben. Technischer Retry ändert weiterhin keine Auftragsdaten. Manuelles Ausstellen und Erneuern von Einladungen versendet ebenfalls eine Nachricht und ist nur in Dispatched erlaubt. In Approved wird eine solche Aktion vor jeder Änderung an Zugang, Postausgang oder Historie abgelehnt; die Oberfläche deaktiviert sie. Erst der reguläre Versand schaltet den Portalzugang frei und erzeugt die erste Einladungswelle.
  5. Fristen/Antworten: Persistente Due Actions und ABP Background Workers lösen fällige Aktionen aus; Teilnehmer legen ihre Stellungnahme über das Benutzerkonto ihrer Institution innerhalb der wirksamen Frist an oder ergänzen sie um einen neuen unveränderlichen Beitrag. Einen Gastpfad gibt es seit RAL-QM-HP-31 nicht mehr. Seit RAL-QM-HP-38 ist die Stellungnahme eine von vier Kategorien oder der Verzicht, und Änderungswünsche und Ablehnung – die Einwände – brauchen eine Begründung von 15 Zeichen oder eine eigene Anlage (HearingResponseRules). Erstabgabe und Ergänzung sind je ein Aufruf; Ablauf in §6.4. Das Fenster ist am UTC-Fristtag offen und ab dem Folgetag geschlossen; ein versandtes Verfahren ohne wirksame Frist hat kein Fenster (ResponseDeadlineMissing). RAL-QM-HP-40 liefert die vollständige geordnete Antwortfolge: Rücklauf, Bescheidung und Abschluss wählen zuerst den jüngsten Beitrag je Teilnahme. Historische Anlagen und Bescheidtexte bleiben am ursprünglichen Beitrag lesbar. Ein wiederholter identischer Auftrag liefert denselben Beitrag; neue Inhalte benötigen eine neue Auftragskennung. Der alte Änderungsweg ist gesperrt. Consents (RAL-QM-HP-39) prüft beide aktuellen Fassungsbestätigungen und speichert den Nachweis in derselben Abgabetransaktion. Die drei fristgesteuerten Nachrichten – erste Erinnerung, zweite Erinnerung, Fristablauf-Nachricht – gehen an die offenen Beteiligungen: nicht zurückgezogen, ohne Stellungnahme und seit RAL-QM-HP-5 (E6) ohne entzogenen Zugang. Die Auswahl liegt an einer Stelle (HearingDueActionProcessor.OpenTargets). Eine Aktion, die keine offene Beteiligung findet, schließt trotzdem als ausgeführt ab; nach der Wiederfreigabe wird deshalb nichts nachgeholt, später fällige Aktionen erreichen die Institution wieder. Entschieden wird beim Einreihen in den Postausgang; eine schon eingereihte Nachricht geht noch hinaus. Jede individuelle Verlängerung (RAL-QM-HP-40) wird an der Beteiligung mit UTC-Fristtag, Begründung und Revision gespeichert. Der wirksame Tag ist das Maximum aus Verfahrens- und Individualfrist und bleibt vollständig offen. Eigene revisionsgebundene Aktionen verhindern verfrühten Ablauf; veraltete Jobs erzeugen keine Nebenwirkung. Zugangsentzug und Lifecycle bleiben vorrangig. Eine Verlängerung löscht nur die abgeleitete Keine-Aussage-Markierung, nie Antworten oder bisherige Historie. Seit RAL-QM-HP-33 gehen Erinnerungen nur in Dispatched hinaus (ein in Adjudication noch beanspruchter Auftrag endet ohne Nachricht). Sie enthalten ausschließlich die aktuell freigegebenen Unterlagen als authentifizierte, tokenfreie Portalverweise der jeweiligen Beteiligung; jeder Download prüft den aktuellen Grant erneut. Jeder Erinnerungslauf hinterlässt einen System-Verlaufseintrag ReminderRunCompleted mit Anzahl und Namen der tatsächlich erinnerten Institutionen (Vertretungskopien zählen nicht), und der Referent kann ausgewählte offene Beteiligungen von Hand erinnern (HearingDispatchAppService.RemindParticipantsAsync, Recht .Dispatch, nur in Dispatched und im wirksamen Fenster; jedes Ziel wird vor dem ersten Zugangsdatum geprüft, eine nicht offene Beteiligung weist den ganzen Aufruf mit Namen ab; die Zeile trägt den Idempotenzschlüssel der Vorgangskennung, dieselbe Kennung reiht nie zweimal ein). Mit gesetzter zweiter Frist plant der Planer zusätzlich die verfahrensweite Aktion NoStatementCheck auf den ersten Augenblick nach dem UTC-Fristtag der zweiten Frist; sie hält für jede dann noch offene, erreichbare Beteiligung (nicht zurückgezogen, ohne Stellungnahme, ohne Zugangsentzug, ohne offene Störung) einmalig HearingParticipant.NoStatementRecordedAtUtc fest, mit System-Verlaufseintrag je Beteiligung, und versendet nichts. Ohne zweite Frist entsteht die Aktion nicht, und die Akte nennt das als Hinweis. „Keine Aussage" ist ein eigener Beteiligungsstand der Rücklaufübersicht und des Exports, kein Statuswert und kein Bestandteil des offenen Bestands; eine spätere interne Nacherfassung (RecordInternallyAsync) ersetzt ihn im selben Speichervorgang durch die verspätete Rückmeldung. Bescheidung, Abschluss- und Abbruchnachricht laufen nicht über diese Auswahl und bleiben unverändert. Auch von Hand geht keine Einladung an eine Beteiligung mit entzogenem Zugang: Ausstellen und Erneuern weist der Dienst mit InvitationNotAllowedWhileAccessRevoked ab (HearingInvitationAppService.EnsureIssuableAsync), die Einladungskarte lässt beide Aktionen ruhen und nennt den Grund – sonst erhielte die Institution ein amtliches Schreiben mit Portallink zu einem Verfahren, das ihre Konten nicht öffnen können. Der HearingDueActionProcessor öffnet seine ABP Units of Work selbst, sowohl beim periodischen Worker als auch beim manuellen Prüflauf. Lesen, Recovery und Claim verwenden kurze, eigene Scopes; ein Claim ist vor der Ausführung festgeschrieben. Einladung, Postausgang, Hearing-Änderung und erfolgreicher Aktionsabschluss bilden je Aktion eine Transaktion. Nach einem Fehler wird diese zurückgerollt und der Retry-Status mit erneut geprüftem Claim in einer frischen Unit of Work gespeichert. Ein konkurrierend verlorener Claim hinterlässt so keinen fehlerhaften EF-Zustand für die folgenden Aktionen. Die Ausführung hält den gelesenen Verfahrenszustand mit RepeatableRead bis zum Commit stabil, ohne Serializable-Bereichssperren für andere Verfahren. Ist das Verfahren bereits Completed oder Cancelled, endet auch eine vorher beanspruchte Aktion ohne neue Einladung, Nachricht oder Verlauf. Ein später konkurrierender Abschluss wartet oder löst einen vollständigen Rollback mit Retry aus. Stellt der Prozessor eine Erinnerung für ein Verfahren mit gesetzter zweiter Frist in den Postausgang, deren wirksame Frist die zweite Verfahrensfrist ist, schreibt er diese fest (SecondResponseDeadlineFixedAtUtc, RAL-QM-HP-29 AC-7): die Nachricht nennt diese Frist, ab da verweigert das Aggregat jede Änderung (SecondDeadlineFixedByReminder), und die Detailseite bietet sie nicht mehr an. Abschlusserinnerung (RAL-QM-HP-43): Der vollständige Bescheidungsversand nach RAL-QM-HP-46 plant in derselben Transaktion genau eine ClosureCheck-Aktion mit DueAtUtc = AdjudicationFullyDispatchedAtUtc + ClosureReminderDays. Eine Teilwelle plant nichts: sie setzt nur den Erstversand (AdjudicationDispatchedAtUtc), erst die Welle, die die letzte erforderliche Beteiligung bedient, setzt den Zeitpunkt der vollständigen Übermittlung. Die Dauer (auch eine fehlende Gruppe) wird einmalig festgeschrieben; spätere Standards gelten für neue Pläne. Der bestehende Worker plant vollständig beschiedene Altdaten ohne Auftrag einmalig nach, jeweils in eigener Transaktion. Fehlende Dauer hinterlässt eine stornierte Aktion; ungültige Importwerte eine fehlgeschlagene Aktion mit sichtbarer, ausdrücklich wiederholbarer Störung. Am UTC-Grenzzeitpunkt prüft die Ausführung den aktuellen Verfahrensstand und die aktuelle Zuständigkeit über Authorization.Contracts. Nicht erreichbare/inaktive Zuständige führen zur einmaligen Eskalation an alle aktiven, eindeutig gebundenen Geschäftsführungskonten mit genau einer Produktrolle und gültiger E-Mail; die Meldung verlangt Zuständigkeitswechsel, überträgt aber nichts. Ohne Empfänger entsteht eine wiederholbare Störung. Enqueue, Systemverlauf und Executed werden gemeinsam gespeichert. Interne Nachrichten (ClosureReminder) besitzen keinen Institutionszweck, kein Anschreiben und keine Teilnehmer-/Zugangsbindung; der DE/EN-Text enthält Nummer, Gütezeichen, Bezeichnung und den permissiongeschützten Bearbeitungslink. SMTP-Retries verwenden denselben Outboxauftrag; erst die erfolgreiche Transportübergabe setzt dessen SentAtUtc.
  6. Adjudication-Fenster (nur zweistufige Abschlussvariante): Ab Eintritt in Adjudication dürfen bis zum ersten Bescheidungsversand nur als Endfassung markierte Dokumente additiv angelegt, ersetzt oder freigegeben werden; die ursprünglich freigegebene Komposition bleibt nachvollziehbar.
  7. Abschluss: Bei zweistufiger Variante schließt der Bescheidungsversand das Fenster, fixiert die versendete Komposition samt Prüfsumme; das Verfahren bleibt bis zum ausdrücklichen Abschluss in Adjudication. Bei einstufiger Abschlussvariante (US-33) gehen Bescheidung und Endfassung in einem Vorgang hinaus, ohne den Adjudication-Status gesondert zu durchlaufen. Seit RAL-QM-HP-51 schließt der Referent auf beiden Wegen ab; beide verlangen eine verfügbare Endfassung, erzeugen die interne Rückmeldungsübersicht und geben die Endfassung den Empfängern nach Regel 4 im Portal frei – Ablauf und Guard-Reihenfolge unter Abschluss und Endfassung. Abbruch (RAL-QM-HP-42): Die Geschäftsführung (HearingsPermissions.Abort, nur RALQMManagement) beendet ein laufendes Verfahren (Dispatched, Adjudication) mit einer Pflichtbegründung, die seit RAL-QM-HP-42 die amtliche ist (IsCancellationReasonOfficial): Sie geht in der Abbruchmitteilung an jede angeschriebene Beteiligung – die aktiven, Zugangsentzug eingeschlossen, und zurückgezogene mit früherer Postausgangszeile –, zusammen mit dem tokenfreien Link auf die lesende Teilnahmeansicht; einen gebundenen Alttext ohne {{Begruendung}} ergänzt der Renderer um den Begründungsblock. Transition, Widerruf von Einladungen, Kontobindungen, Grants und Due Actions, Aufträge und Verlaufseintrag Aborted laufen in einer Transaktion; Wiederholung und konkurrierender Abbruch erzeugen keinen zweiten Auftrag, ein Transportfehler bleibt am Auftrag. Cancel vor dem Versand (Draft, PendingApproval) sendet nichts und hält den Grund intern; Approved ist von beiden ausgeschlossen. Der Endzustand ist schreibgeschützt und bleibt in der Arbeitsliste, bis RAL-QM-HP-44 ein Archivkennzeichen bringt. Archivieren und Zurückholen (RAL-QM-HP-44): Ein beendetes Verfahren (Completed, Cancelled) erhält durch Referent oder Geschäftsführung (HearingsPermissions.Archive) das Archivkennzeichen IsArchived mit Actor und UTC-Zeit - kein Status, keine Wiedereröffnung, keine Löschung. Verfahrensübersicht, Arbeitsvorrat und Export schließen archivierte Verfahren aus, auch mit explizitem Statusfilter; die Archivseite zeigt ausschließlich archivierte. Zurückholen verlangt das Kennzeichen und eine Pflichtbegründung, die nur im ABP-Audit-Log des Aufrufs steht; Endzustand, Schreibschutz und entzogene Zugänge bleiben. Beide Befehle schreiben je genau einen Verlaufseintrag (Archived, RestoredFromArchive) in derselben Transaktion; Stempelkonflikt oder Doppelaktion lassen nichts zurück. Öffentliche Anzeige (RAL-QM-HP-47): Ein freigegebenes oder versendetes Verfahren (Approved, Dispatched) erhält durch Referent oder Geschäftsführung (HearingsPermissions.Publish) das Kennzeichen IsPublished und beim ersten Mal die unveränderliche öffentliche Kennung PublicId. Sichtbar ist es, solange Kennzeichen UND einer der beiden Status gelten (HearingPublicationRule); jede öffentliche Lesung wertet beides in SQL aus. Der Wechsel in die Bescheidung, Abschluss oder Abbruch beenden die Anzeige ohne Job und ohne Änderung am Kennzeichen; die Rücknahme der Freigabe nimmt das Kennzeichen zusätzlich zurück (Eintrag Unpublished), sodass eine erneute Freigabe nicht automatisch wieder anzeigt. Zurücknehmen ist in jedem Status möglich. Kennzeichnen und Zurücknehmen schreiben je einen Verlaufseintrag (Published, Unpublished). Die Website gleicht über den Bestandsabruf ab (Pull, siehe 6.4); einen Push an die Website gibt es nicht. Seit RAL-QM-HP-78 liest sie seitenweise und quittiert danach, was sie übernommen hat (POST /api/gz/hearings/public/receipts). Bleibt eine Änderung länger als die Frist unbestätigt oder kommt so lange keine Quittung, meldet der Sync-Worker eine Störung (ADR-20).
  8. Neben ABP Audit Logging führt Hearings eine PII-arme fachliche Historie (RALQMHearingHistoryEntries); Freitexte, Dokumentinhalte und Zugangstokens erscheinen dort nie.

Details zu Datenownership und Tabellen stehen in Querschnittliche Konzepte §2.

Beteiligungsstand der Auswertung (RAL-QM-HP-36)

Die Rückmeldungsseite und der Reiter „Rücklauf“ zeigen je Institution genau einen Stand. Er wird bei jedem Laden aus den gespeicherten Sachverhalten abgeleitet und nicht gespeichert (HearingParticipationStateRule); es entsteht kein weiterer Verfahrens- oder Teilnehmerstatus. Treffen mehrere Sachverhalte zu, gewinnt der obere (SPEC-RAL-QM-HP-36, Regel 5):

Rang Stand Sachverhalt, gelesen aus
1 Rückmeldung vorliegend / Verzicht jüngste Abgabe der Teilnahme (HearingResponseSequence): Stellungnahme oder Verzicht
2 Zugang entzogen HearingParticipant.AccessRevokedAtUtc
3 Zustellung fehlgeschlagen Postausgang: die zuletzt erzeugte Mail an den Ansprechpartner ist endgültig gescheitert, aufgegeben oder als unzustellbar gemeldet
4 Keine Aussage HearingParticipant.NoStatementRecordedAtUtc
5 Erinnert Teilnehmerstatus Reminded
6 Eingeladen Postausgang: es gibt eine Einladung an den Ansprechpartner der Beteiligung
7 Hinzugefügt nichts davon
– Zurückgezogen Teilnehmerstatus Withdrawn; am Ende der Liste und in keiner Zahl

Ein zutreffender Sachverhalt, der nicht der Stand ist, steht als Hinweis in der Zeile (Zugang entzogen, Zustellung fehlgeschlagen, Keine Aussage, Verspätet abgegeben; bei Zurückgezogenen die frühere Abgabe). Für „Zustellung fehlgeschlagen“ zählen die Vertretungskopie (Schlüsselende :dep), die Mail an zusätzliche Empfänger (Schlüsselanfang recipient:), ersetzte Zeilen und laufende Wiederholungen nicht; ein korrigierter Folgeauftrag zählt, wenn seine Ursprungszeile eine Mail an den Ansprechpartner war (HearingParticipationMails). „Eingeladen“ liest die Einladung an den Ansprechpartner im Postausgang, weil kein Übergang den Teilnehmerstatus Invited setzt – gleich mit welchem Transportergebnis; eine Vertretungskopie oder eine Mail an zusätzliche Empfänger allein lädt niemanden ein.

„Offen“ hat zwei Bedeutungen. Für die Auswertung – Kennzahl „offen“ und Filter „Nur offene Beteiligungen“ – ist eine Beteiligung offen, wenn sie keine Abgabe hat, nicht als „Keine Aussage“ vermerkt und nicht zurückgezogen ist, auch mit entzogenem Zugang. Für Erinnerungen und Fristablauf (HearingDueActionProcessor.OpenTargets, Schritt 5) gilt zusätzlich: ohne entzogenen Zugang. Die Auswertung entscheidet nicht, wer erinnert wird.

Der Rücklauf zählt die nicht zurückgezogenen Institutionen mit Abgabe – Stellungnahme oder Verzicht – gegen die Beteiligten ohne Zurückgezogene. Die Filter (offen, Stand, Kategorie der jüngsten Abgabe) wirken zusammen, werden auf dem Server ausgewertet und schmälern nur die Zeilen; die Zahlen beschreiben immer das ganze Verfahren. Der Export liefert dieselben Zeilen unter denselben Filtern. Laden, Filtern und Exportieren sind eine Momentaufnahme ohne Push, ändern keine Daten und versenden nichts.

Kontaktmeldung (RAL-QM-HP-41)

Eine Institution meldet im laufenden Verfahren eine andere Kontaktperson; der Referent entscheidet darüber. Die Meldung ist ein eigener Datensatz neben dem Verfahren (HearingContactReport), kein weiterer Verfahrens- oder Teilnehmerstatus. Sie ist offen und erreicht genau einen Endzustand; je Beteiligung ist höchstens eine Meldung offen (SPEC-RAL-QM-HP-41).

Handlung Wer Verfahrensstatus Wirkung
Melden Institutionskonto Dispatched Meldung „offen“; Empfänger, Stammdaten und Konten bleiben
Zurückziehen jedes Konto der Institution Dispatched „zurückgezogen“; eine neue Meldung ist möglich
In Stammdaten übernehmen Referent mit Kontaktpflegerecht Dispatched, Adjudication ein neuer aktiver Kontakt der Institution, er wird Empfänger dieser Beteiligung; „übernommen“
Vorhandenen Kontakt verwenden Referent Dispatched, Adjudication ein aktiver Kontakt derselben Institution mit dieser E-Mail wird Empfänger, kein neuer Kontakt; „übernommen“
Nur für dieses Verfahren übernehmen Referent Dispatched, Adjudication Empfängerstand ohne Stammdatenkontakt (ADR-14); „übernommen“. Nicht mit der E-Mail des Stammdatenkontakts, der schon Empfänger ist
Ersetzen Referent Dispatched, Adjudication eines der drei Ziele mit einer anderen, vollständig erfassten Person; „ersetzt“ mit Person, Ziel und optionaler Notiz
Verwerfen Referent jeder Status „verworfen“ mit optionaler Notiz; sonst ändert sich nichts

Die Rückmeldefrist spielt beim Melden keine Rolle. Nichts schließt sich von selbst: Eine beim Verfahrensende offene Meldung bleibt offen, bis der Referent sie verwirft. Bescheidversand, Abschluss und Abbruch werden nicht aufgehalten; an diesen vier Stellen nennt ein Hinweis die Institutionen mit offener Meldung (IHearingContactReportAppService.GetOpenNoticeAsync, nur Namen) für den Stand beim Öffnen der Seite. „Meine Verfahren“ öffnet bei einem versandten Verfahren die Karte der Teilnahmeansicht direkt.

Vor einer Entscheidung liest die Verfahrensakte ihre Grundlage (PrepareDecisionAsync): die Stände von Meldung, Verfahren und Institution, ob das Verfahren noch läuft, Kontakte derselben Institution mit derselben E-Mail (aktiv oder inaktiv, ohne Beachtung der Groß-/Kleinschreibung), ob ein gewählter zusätzlicher Empfänger dieses Postfach trägt, das Kontaktpflegerecht des Aufrufers und die aktiven Funktionen. Übernehmen und Ersetzen laufen in einer serialisierbaren Transaktion mit allen Prüfungen vor dem ersten Speicherschritt: Meldung offen und unverändert, Verfahrensstand, Status und aktive Beteiligung, kein gleiches Postfach, Rechte, Institution aktiv und unverändert, Funktion aktiv. Erst danach folgen der neue Kontakt (Institutions-Vertrag „einen Kontakt hinzufügen“), der Empfängerwechsel der Beteiligung, der Endzustand der Meldung und ein Verlaufseintrag. Scheitert ein Teil, bleibt alles unverändert. Zurückziehen und Entscheiden senden den gelesenen Stand der Meldung; die zuerst gespeicherte Handlung gilt, die andere endet als Konflikt, auch als Deadlock-Opfer. Ein wiederholter Aufruf trifft auf den Endzustand und legt nichts doppelt an.

Die nächste Nachricht des Verfahrens geht an den neuen Empfänger; eingereihte Nachrichten und frühere Versandstände bleiben. Ein Empfänger ohne Stammdatenkontakt erhält keine Vertretungskopie. Der Verlauf trägt je Handlung einen Eintrag (ContactReportSubmitted, …Withdrawn, …Adopted, …Discarded, …Replaced) mit Akteur, Zeitpunkt und Beteiligung, bei Übernahme und Ersetzen mit Ziel und Kontaktbezügen, nie mit Angaben der Person oder der Notiz. Konten, Bindungen und Sitzungen bleiben von allem unberührt.

Folgeregeln vom 30.09.2026 (ADR-09)

Vor dem Erstversand darf die Geschäftsführung eine Freigabe nach Draft zurücknehmen; Korrektur erfordert anschließend erneute Einreichung und Vier-Augen-Freigabe. Frühere Entscheidungen bleiben. Interne Entscheidungsbenachrichtigungen ohne erreichbaren Zuständigen pausieren 72 Stunden nach dem ersten Empfängerfehler. Die ausdrückliche Fortsetzung legt einen verknüpften Folgeauftrag an.

Ein verspäteter automatischer Reminder prüft den wirksamen individuellen UTC-Fristtag erneut und endet bei geschlossenem Fenster ohne Nachricht. Der Erinnerungsbericht ist sofort nach Einreihung im geschützten internen Portal verfügbar. Nach Transportabschluss, spätestens nach 24 Stunden als Zwischenbericht, erhält der bei Auftragserstellung festgelegte Referent einen Link. Offene Transportaufträge werden weiter versucht; spätere Berichtabrufe zeigen ihren aktuellen Stand. Zusätzliche gewählte Kontakte und Vertretungen erhöhen die Nachrichtenzahl, nicht die Zahl der Beteiligungen. Die manuelle Mehrfachauswahl wird vor dem Auftrag bestätigt und vollständig validiert.

Abschluss und Endfassung (RAL-QM-HP-51)

Beide Abschlusswege – zweistufig Complete (aus Adjudication bzw. als Ausnahmeweg ohne relevante Stellungnahme aus Dispatched) und einstufig CompleteSingleStage im kombinierten Vorgang – laufen durch denselben Zweiphasen-Ablauf (SPEC-RAL-QM-HP-51, Bausteine in 5.2.3). Den Abschluss führt der Referent aus: direkt mit Complete, kombiniert mit Adjudicate UND Complete.

sequenceDiagram
    actor R as Referent (RALQMCaseOfficer)
    box transparent RALQM-Host
        participant A as HearingAppService /
HearingAdjudicationAppService participant C as HearingClosureManager participant G as HearingClosureReportGenerator end participant D as CAF Documents R->>A: Abschließen (Stempel) A->>A: Guards 1–7, ohne Nebenwirkung A->>C: PrepareClosureAsync C->>G: Rückmeldungsübersicht erzeugen G->>D: CreateAsync (PDF) A->>C: AttachClosureReportAndGrantFinalVersion (try-Block) C->>C: AttachClosureReport, Endfassungs-Grants A->>A: einstufig: Bescheidungswelle ausführen A->>A: Transition Complete / CompleteSingleStage A->>C: CompleteClosureAsync C->>C: 40 und 70 in den Postausgang,
Verlauf 810/820, Widerruf Note over A,D: Fehler nach CreateAsync: Bericht in derselben
Unit of Work löschen, dann Rollback

Guard-Reihenfolge. Auf beiden Wegen gleich und ohne jede Nebenwirkung; ältere Abweisungen behalten ihre Codes:

  1. Recht und Verfahrensstempel (ConcurrencyStamp).
  2. Aggregat-Vorabprüfung Hearing.EnsureCompletable bzw. EnsureCompletableSingleStage: Variante, Status, Bescheidungserfordernis, Rückmeldephase, unbeantwortete Einwände – dieselben Prädikate, Codes und Reihenfolge wie die Transition, die sie selbst aufruft.
  3. Nur einstufig: Bescheidungswelle planen (PlanAdjudicationWaveAsync, rein; UnansweredObjections, AdjudicationReplyMissing, Reihenfolge „unbeantwortet vor entzogen“ unverändert). Ausgeführt wird sie erst nach der Vorbereitung.
  4. Endfassung vorhanden: mindestens eine aktive, nicht ersetzte Abschlussanlage, sonst ClosingAttachmentMissing – auch auf dem Ausnahmeweg und bei leerer Empfängermenge.
  5. Endfassung lesbar: Jede aktive Abschlussanlage hat einen Deskriptor (IDocumentContentPort.DescribeManyAsync), und jeder unterschiedliche Endfassungsinhalt lässt sich über IDocumentContentPort.OpenAsync öffnen (Datenstrom sofort freigegeben; der Datenbank-Blob-Provider lädt den Inhalt dabei vollständig in den Speicher, siehe SPEC-RAL-QM-HP-51 Offenlegung 20). Meldet das Öffnen „Inhalt fehlt“, folgt derselbe Code mit reason = contentUnavailable und eine Warnung mit der Dokument-Id (Owner-Review !175: Deskriptor ohne Blob). Andere Fehler laufen als Betriebsfehler durch.
  6. Vorlage 70: Mit mindestens einem Endfassungsempfänger mit wirksamem Endfassungs-Grant (Regel 7) muss die Endfassungsbenachrichtigung gebunden sein, sonst TemplateNotResolvable (purpose 70); siehe Ergänzungsbindung.
  7. Bericht anhängbar, als reine Aggregatprüfung: Fenster (Dispatched/Adjudication), höchstens ein Bericht je Verfahren, Anzeigename frei. Ist Rueckmeldungsuebersicht_<Nummer>.pdf belegt, wählt die Vorprüfung deterministisch den ersten freien Namen Rueckmeldungsuebersicht_<Nummer>_2.pdf, _3 … – keine Abweisung, die den Abschluss dauerhaft blockieren würde.

Nebenwirkungen, erst danach und in einer Unit of Work:

  • PrepareClosureAsync: Empfängermenge nach Regel 4, beschränkt auf Empfänger mit wirksamem Endfassungs-Grant (Regel 7, 9); Berichtsmodell (alle Beteiligungen mit Status, je Beteiligung Eingangszeit, Art, Verspätung, Text und wirksame Bescheidung in Eingangsreihenfolge); Bericht über CreateAsync.
  • Unmittelbar danach, schon im try-Block des Anwendungsdienstes: AttachClosureReportAndGrantFinalVersion – Hearing.AttachClosureReport unter dem vorgeprüften Anzeigenamen (Zweck ClosureReport, Herkunft Generated, keine Grants, nie Pflichtdokument); Endfassungs-Grants je aktiver Abschlussanlage × Empfänger. Ein Paar mit bereits widerrufenem Grant wird übersprungen (Regel 9: keine Reaktivierung), aktive Grants bleiben idempotent, fremde Grants auf Abschlussanlagen bleiben bestehen (E3).
  • Nur einstufig: die geplante Bescheidungswelle ausführen.
  • Transition Complete bzw. CompleteSingleStage; sie widerruft alle Grants mit HearingCompleted, die nach RAL-QM-HP-30 lesbar bleiben.
  • CompleteClosureAsync: Abschlussmitteilung (40) an die aktiven Beteiligten. Die Endfassungsbenachrichtigung (70; credential-frei, Idempotenzschlüssel Sequenz 1, Vertretungskopie, ohne Begleitdokument) und FinalVersionDelivered (810, je Empfänger, Subjekt Beteiligung) erhalten nur Empfänger mit mindestens einem wirksamen Endfassungs-Grant – einem vor der Transition aktiven oder neu erteilten. Wurden alle Grants eines Empfängers nach Regel 9 übersprungen, entstehen für ihn weder 70 noch 810 (Regel 7 bleibt wahrheitsgemäß). Danach ClosureReportCreated (820, einmal, Subjekt Bericht) und der Widerruf laufender Artefakte.

Einstufig erhält ein Empfänger damit drei Nachrichten: Bescheid, Abschlussmitteilung und Endfassungsbenachrichtigung. Das folgt der Trennung in RAL-QM-HP-51 O1 und ist als Folgefrage beim PO offengelegt. Im Portal gibt HearingPortalRules.IsDocumentReleased nach Completed alles frei außer ClosureReport; dieselbe Regel gilt für Liste, Download und Erinnerung, ein verirrter Grant auf den Bericht öffnet nichts.

Fehler und Wiederholung. Eine Abweisung in den Schritten 1–7 hinterlässt nichts. Scheitert ein Schritt nach CreateAsync – etwa eine Abweisung beim Rendern der einstufigen Welle oder der Endfassungsbenachrichtigung –, löscht der Abschluss den gerade abgelegten Bericht noch in derselben Unit of Work über IDocumentFilingPort (Löschreservierung und Abschluss der Löschung, Muster HearingDocumentAppService.CompensateUploadAsync) und wirft weiter; die Transaktion rollt Verfahren, Grants, Aufträge und Verlauf zurück. Mit dem Datenbank-BLOB-Provider ist der Bericht ohnehin Teil der Transaktion. Mit dem Dateisystem-Provider räumt der Documents-Baustein selbst nach: Er merkt jede über den Inhaltsport neu geschriebene Datei an der Unit of Work vor und entfernt nach deren Scheitern – auch bei einem Fehler beim Speichern oder beim Commit selbst, an dem der Fehlerzweig selbst scheitert und nur eine Warnung protokolliert – jede Datei ohne committeten Deskriptor, Bericht wie Begleitbriefe 30/40 (Documents-Vertrag). Was ein Prozessabbruch zwischen Ablage und Bereinigung zurücklässt, entfernt seit RAL-QM-HP-81 der wiederkehrende Abgleichslauf des Documents-Bausteins (Risiken). Eine Wiederholung nach dem Commit weist der Statusguard ab, die Outbox-Schlüssel sind eindeutig – es entsteht weder ein zweiter Bericht noch ein zweiter Auftrag.

6.3 Externe Kontoprovisionierung

Zwei getrennte, beide fail-closed arbeitende Pfade nutzen denselben kontrollierten Keycloak-Admin-Port (IKeycloakAdminClient/KeycloakAdminHttpClient im Feature Authorization) über den dedizierten vertraulichen Client ral-qualitymarks-provisioning (nur Rolle manage-users, kein Browser-/Direktzugriffs-Flow).

sequenceDiagram
    actor U as Berechtigter Benutzer
    box transparent RALQM-Host
        participant UI as Institutions/Authorization.Blazor
        participant Auth as Authorization AppService
    end
    participant Kc as Keycloak Admin API

    alt Kontakteinladung (aus Kontaktsuche)
        U->>UI: Kontakt in institutionsübergreifender
Suche wählen UI->>Auth: stabile Kontakt-ID übergeben Auth->>Kc: Konto per E-Mail suchen alt kein/eindeutiges Match Auth->>Kc: Konto anlegen bzw.
Subject bestätigen Kc-->>Auth: sub Auth-->>UI: Einladung versendet
(keine automatische Fachrolle) else mehrdeutig Auth-->>UI: Fehler AmbiguousEmail
(geschlossen abgelehnt) end else Mitarbeitendenkonto (/administration/…) U->>UI: Name, E-Mail, genau eine
GZ-Produktrolle UI->>Auth: Anlage-/Wiedereinladungsauftrag Auth->>Kc: Subject strikt bestätigen Kc-->>Auth: sub Auth->>Auth: lokale ABP-Projektion
+ Rollen-Grant setzen Auth-->>UI: Konto aktiv, Wiedereinladung nur
bei ausdrücklicher Wahl end
  1. Kontaktbasierte Einladung: So entstehen die dedizierten Institutionskonten — stets RAL-seitig ausgelöst (aus der institutionsübergreifenden Kontaktsuche), nie per Selbstregistrierung; je Institution erhalten mehrere Kontakte je ein eigenes Benutzerkonto. Die UI übergibt nur die stabile Kontakt-ID an den Provisionierungsport. E-Mail-Gleichheit erteilt keine Fachberechtigung — Identitäten werden über sub korreliert. Ein bereits vorhandenes eindeutiges Konto erhält eine erneute Einladung statt eines Dublettenkontos; es wird keine automatische Hearing-Teilnahme vergeben. Seit RAL-QM-HP-18 läuft der Pfad über den Kontodienst (ProvisionInstitutionAccountAsync, Recht RALQM.Accounts.InviteInstitution): der Ansprechpartner muss aktiv sein und eine E-Mail haben, das Konto erhält die Produktrolle Institution (nur Portalrecht) und eine Bindung an Institution und Ansprechpartner in derselben Unit of Work; ein internes Konto unter der Adresse wird abgewiesen (RoleKindMismatch), ein bereits gebundener Ansprechpartner ebenso (AlreadyBound).
  2. Mitarbeitendenkonto: einziger Anlage-/Wiedereinladungseinstieg ist die GZ-Seite /administration/mitarbeitendenkonten (Recht RALQM.Accounts.Invite, nur Systemadministrator). Sie verlangt Name, E-Mail und genau eine interne Produktrolle, bestätigt das Keycloak-Subject strikt und weist die Rolle erst der lokalen ABP-Projektion zu. Wiederholte Aufrufe versenden ohne ausdrücklich gewählte Wiedereinladung keine neue E-Mail. Rollenwechsel bleibt in der Kontoart (intern/Institution) und lässt dem letzten aktiven Systemadministrator bzw. der letzten Geschäftsführung die Rolle. Sperren setzt das Keycloak-Konto auf enabled=false, beendet dessen Provider-Sitzungen, erneuert den Security Stamp und widerruft alle CAF-Sitzungen des Subjects — die nächste Aktion des Kontos landet auf der Anmeldung; das eigene Konto und das letzte einer Führungsrolle sind nicht sperrbar. Kennungswechsel (Personalwechsel) setzt E-Mail und Name in Keycloak und lokal neu, entfernt das Passwort, beendet die Sitzungen und lädt die Nachfolgeperson ein; Subject, Anmeldename (Alias — die Admin-API ändert ihn unter editUsernameAllowed=false nicht), Institution, Verfahrenssicht und Verlauf bleiben. Papierkorb statt Löschen: Soft Delete mit Provider-Sperre, wiederherstellbar aus /administration/mitarbeitendenkonten/papierkorb; einen endgültigen Löschpfad gibt es nicht. Jede Mutation schreibt einen Verlaufseintrag (RALQMStaffAccountHistory) mit Akteur. Der Einladungszustand der Übersicht (offen bis / angenommen / abgelaufen) kommt aus dem Sendezeitpunkt (ExtraProperty) und den Keycloak-Zuständen (requiredActions); ein nicht erreichbarer Provider lässt ihn „unbekannt“ statt die Übersicht scheitern.
  3. Fehlerfälle scheitern geschlossen und sind stabil kodiert: ExternalAccountProvisioningErrorCodes (RALQM.Authorization:Provisioning:Disabled| AmbiguousEmail|SubjectConfirmationFailed|InvalidSubject|EmailBoundToOtherAccount) für den Kontaktpfad, StaffAccountErrorCodes (RALQM.Authorization:StaffAccounts:InvalidInput| InvalidRole|LocalUserNotFound|ProductRoleNotFound|NotExternallyLinked|ConcurrencyConflict| CannotChangeOwnAccount|LastAdministrator|LastManagement|RoleKindMismatch|ContactNotFound| ContactInactive|ContactWithoutEmail|AlreadyBound|EmailAlreadyUsed|NotInTrash|InTrash| InvitationAlreadyAccepted) für Mitarbeitenden- und Institutionskonten.
  4. Die ABP-eigene Benutzerseite ist für keinen der beiden Pfade ein unterstützter GZ-Weg und wird von den GZ-Seiten nicht verlinkt.

  5. Aktivierungszustimmung (RAL-QM-HP-39): Nach der angenommenen Keycloak-Einladung führt der erste Portalaufruf auf /portal/zustimmung. Die Person akzeptiert die Nutzungsbedingungen und nimmt die Datenschutzhinweise der geltenden Fassungen zur Kenntnis; erst der dabei gespeicherte Aktivierungsnachweis gibt den Fachzugang frei. Ohne veröffentlichte Fassungen bleibt der Schritt gesperrt.

6.4 Dokumentfreigabe

ABP File Management besitzt Dateimetadaten und BLOB-Inhalt. IDocumentContentPort bleibt permission-frei; der Hearings-Anwendungsfall autorisiert vollständig vor jedem Inhaltsaufruf. Seit RAL-QM-HP-31 bestehen im Host zwei Produktpfade: interne Berechtigungen und feste Institutionskonten.

sequenceDiagram
    actor P as Institutionskonto
    participant H as Hearings
    participant A as Authorization-Contract
    participant D as Documents-Contract
    P->>H: Liste / Detail / Mutation / Download
    H->>A: GetCurrentAsync (frische Prüfung)
    A-->>H: Konto-, Bindungs-, Institutions- und Kontakt-ID
    H->>H: aktive Teilnahme ohne Zugangsentzug + Lifecycle + Frist + Grant
    H->>D: Inhalt erst nach Autorisierung
    D-->>P: Inhalt über Hostadapter (no-store)

Authorization liest die lokale Konto-/Rollen-/Subject-Zuordnung, die aktive feste Bindung und den aktuellen Providerzustand einschließlich angenommener Einladung. Unbekannter Zustand schließt den Zugriff. Die Providerprüfung fragt gezielt users/{subject} ab; Transport- und Providerfehler bleiben technische Fehler mit Wiederholen statt einer dauerhaften fachlichen Zugriffsverweigerung. Hearings verwendet ausschließlich diesen prozessinternen Contract; keine fremden Tabellen oder Repositories. Mehrere Konten derselben Institution greifen auf dieselben zulässigen Teilnahmen zu. Übersicht und Detail laden ohne alte Trackingwerte. Schreibvorgänge verwenden Serializable; die Bindungslesesperre bleibt bis zum Commit erhalten, sodass ein konkurrierender Widerruf eine definierte Reihenfolge hat. Vor Verwendung bereits getrackter Hearing-Aggregate wird ihr Stempel mit dem frisch autorisierten Stand verglichen.

Seit RAL-QM-HP-5 (AC-9, AC-10) gehört der Zustand „Zugang entzogen“ der Beteiligung (AccessRevokedAtUtc) zur Prüfkette jeder Portaloperation: Ein Verfahren mit entzogenem Zugang fehlt in „Meine Verfahren“, und Übersicht, Abruf, Abgabe und Änderung werden für jedes Konto der Institution mit derselben neutralen Meldung abgewiesen wie ein fremdes Verfahren – auch nach dem Ende des Verfahrens, sodass die Institution davon nichts mehr erfährt; ein abgewiesener Abruf ruft den Content-Port nicht auf, die gespeicherte Stellungnahme bleibt intern lesbar. Die Freigabe wirkt ab dem nächsten Aufruf ohne neue Einladung. Ein gleichzeitiger Schreibvorgang des Portals wird durch seine Serializable-Transaktion und den Aggregatstempel entweder vor dem Entzug festgeschrieben oder danach abgewiesen; einen Teilzustand gibt es nicht (HearingAccessRevocationRaceSqlServerTests).

Stellungnahme-Anhänge prüfen intern Responses.Read und die vollständige ID-Kette, extern Kontozugang und die eigene institutionsbezogene Rückmeldung. Abgelehnte Downloads rufen den Content-Port nicht auf und liefern 404 mit no-store und nosniff. Fristen und gesonderte Versandfreigaben bleiben bestehen.

Seit RAL-QM-HP-30 liest das Portal auch nach dem Ende eines versandten Verfahrens (HearingPortalRules.IsReadable): lesbar sind Dispatched, Adjudication, Completed und ein Cancelled mit Versandzeitpunkt; ein Abbruch vor dem Versand antwortet wie ein unbekanntes Verfahren, und eine eigene Abbruchmeldung gibt es nicht mehr. Geschrieben wird nur in Dispatched; ein Schreibversuch auf ein beendetes Verfahren erhält dieselbe neutrale Abweisung wie einer in der Bescheidung. Eine Freigabe ist nach dem Ende lesbar, wenn sie aktiv ist oder allein wegen des Endes widerrufen wurde (IsGrantReadable); die Gründe „durch das Ende“ stehen genau einmal in HearingPortalRules.EndRevocationReasons (HearingCompleted, HearingCancelled) und speisen die C#-Regel wie die SQL-Abfrage des HearingPortalAuthorizer. Hand-, Dokument- und Teilnahmewiderruf sowie der Versionsersatz (DocumentSuperseded) bleiben gesperrt, nichts wird reaktiviert. Die Authorizer-Abfrage des Downloads bekommt den Endzustand aus dem bereits autorisierten Aggregat mit, nie aus dem Request. Nach einem Abbruch bleiben die Unterlagenarten der erreichten Abschnitte sichtbar (Anhörungsunterlagen; Bescheidungsanlagen nur bei begonnener Bescheidung; Abschlussanlagen nie), nach dem Abschluss alle. Der Zugangsentzug ist nach dem Ende eingefroren, das erste Öffnen wird auch dann noch als Zustand und Verlaufseintrag festgehalten. Eine fehlende Bindung ergibt vor jeder Abfrage den eigenen Code RALQM.Hearings:PortalAccessMissing, ein fehlendes Portalrecht die ABP-Abweisung des Proxys (Code Volo.Authorization:010001); beide zeigen die Portalseiten als Hinweis „kein Institutionszugang“, die Objektebene behält ihre eine neutrale Abweisung. Ein Schreibversuch, der auf ein inzwischen beendetes Verfahren trifft, führt auf der Seite zu einem erneuten Lesen: lesbar → Leseansicht mit dem Hinweis „Ihre Änderung wurde nicht gespeichert“, sonst die neutrale Meldung.

Seit RAL-QM-HP-41 schreibt das Portal außerdem Kontaktmeldungen (SubmitContactReportAsync, WithdrawContactReportAsync). Es gilt die Prüfkette jeder Portalschreibung – Portalrecht, frisch geprüfte Bindung, eigene aktive Beteiligung ohne Zugangsentzug, Dispatched –, aber ohne die Rückmeldefrist. Außerhalb davon und für eine fremde oder unbekannte Meldung gilt die neutrale Abweisung, und zwar vor jeder Prüfung des Inhalts. Gespeichert wird in HearingPortalWriteTransaction unter der Teilnehmerreservierung; „höchstens eine offene Meldung je Beteiligung“ sichert zusätzlich ein gefilterter eindeutiger Index. Die Teilnahmeansicht liefert die Meldungen der Beteiligung mit Datum und Zustand, ob gemeldet werden kann, und dann die aktive Funktionsliste; gelesen wird wie die Teilnahmeansicht, auch nach dem Ende. Der Portalvertrag trägt weder die interne Notiz noch das Ziel noch die Person, die der Referent stattdessen übernommen hat (Ablauf: 6.2).

Zwei neutrale 404 mit derselben Aussage, aber verschiedener Form: Die Seitenadresse /portal/teilnahme/{id} antwortet bei fremder, unbekannter oder entzogener Kennung mit 404 ohne Inhalt (no-store, nosniff, noindex); die Downloadroute antwortet mit 404 samt JSON-Körper des einen neutralen Codes. Beide sind unabhängig davon, ob die Kennung existiert. HearingPortalParticipationRouteMiddleware (Hearings-Runtime) stellt dem Vertrag die Frage IHearingAccountPortalAppService.IsParticipationReadableAsync – kein Remote-Service, derselbe SQL-Filter wie die Teilnahmeliste – und läuft im Host hinter UseAuthorization und UseRateLimiter; die Seite trägt dafür dieselbe Policy hearing-portal wie der Download-Controller. Ohne Anmeldung greift die Autorisierung der Seite, ohne Portalzugang zeigt die Seite ihren Hinweis, ein technischer Fehler der Prüfung wird als Warnung protokolliert und zur Seite durchgereicht (wiederholbar), eine vom Client abgebrochene Anfrage endet ohne Eintrag.

Betriebshinweis zur Aufrufbremse hearing-portal (HearingPortalRateLimiterRegistration): ein festes Fenster von 20 Aufrufen je Minute und Absenderadresse ohne Warteschlange, das die Vollaufrufe der Teilnahmeseite und die Portal-Downloads gemeinsam zählt; die Navigation innerhalb des Blazor-Circuits zählt nicht. Hinter einem gemeinsamen Ausgang (NAT, Reverse-Proxy mit dessen Adresse) teilen sich alle Konten dieser Adresse das Fenster. Die Ablehnung ist eine nackte 429 ohne Fehlerseite; eine Konfigurationsfläche gibt es bewusst erst auf Anforderung des Betriebs. Im vollen Browserlauf vom 24.09.2026 trat keine 429 auf.

Kontenprojektion, Profil und Institutionsfilter zeigen nur aktive Bindungen. Widerrufshistorie gehört in die gesonderte Bindungsverwaltung. Eine ausdrücklich erneute Kontakteinladung kann mit Verwaltungsrecht dieselbe feste Bindung wiederherstellen; Rücknahmebefehl, Kontenänderung und Historie laufen gemeinsam in einer eigenen Serializable-Unit-of-Work nach dem Vorabcheck. Ein Providerfehler rollt sie zurück; Institution/Kontakt und deaktivierte Konten bleiben geschützt.

Portal-only-Grants leiten interne Komponenten-GETs bereits im Host vor Autorisierung und Blazor-Render zum Portal um. Die Entscheidung verwendet Permissions und ersetzt keinen Circuit-Inhalt während der Initialisierung. Öffentliche Rechtstexte tragen AllowAnonymous; APIs und Nicht-Komponenten-Endpunkte behalten ihre eigene Autorisierung.

Endpoint-Inventar: Fach-APIs und Kontoportal verlangen Anmeldung und Produkt-/Objektprüfung. Der Host setzt eine authentifizierte Fallback-Policy; Routen ohne Endpunkt antworten davor 404 (UnmatchedRouteNotFoundMiddleware), weil die Policy sonst auch unbekannte Pfade anfordern würde. Login, OIDC-Callback, Abmeldung und öffentliche Rechtstexte bleiben ohne Fachdaten erreichbar. RAL-QM-HP-79 ergänzt /Barrierefreiheit als vierten anonymen Rechtstext; die Seite liest ausschließlich Betreiber-/Erklärungskonfiguration und das versionierte Register bekannter Barrieren. Der Rückmeldeweg ist ein E-Mail-Link ohne Anmeldung und ohne Speicherung in der Anwendung. Seit RAL-QM-HP-80 gehören auch /nicht-gefunden und /fehler/{statusCode:int} zum datenfreien anonymen Inventar; sie zeigen ausschließlich die 403/404/500-Vorlage und keinen Inhalt der fehlgeschlagenen Fachroute. Die ABP-Kontoseiten des Anmeldewegs werden per Razor-Pages-Konvention aus PublicAccountPages geöffnet, Kontoverwaltung nicht; Framework-Ressourcen behalten ihre expliziten Authentifizierungsmetadaten; die Circuit-Endpunkte /_blazor* sind öffentlich, autorisiert wird je Komponente. /health/live, /health/ready und /health/status sind explizit öffentlich; Status verlangt zusätzlich die konfigurierte Netzwerkfreigabe. Statische Assets sind öffentlich (Static-Asset-Endpunkte tragen AllowAnonymous). Seit RAL-QM-HP-47 ist die Komponentenseite /anhoerungen/oeffentlich ausdrücklich öffentlich; sie zeigt nur die öffentliche Projektion (Titel, Gütezeichen, Verfahrensart, Frist) der sichtbaren Verfahren. Der Bestandsabruf GET /api/gz/hearings/public[?changedSinceUtc=][&pageSize=][&continuation=] und die Quittung POST /api/gz/hearings/public/receipts (RAL-QM-HP-78) sind nicht öffentlich: er verlangt ein Keycloak-Client-Credentials-Token des eigenen Bearer-Schemas HearingPublicationFeed mit Audience und einem zugelassenen azp (ADR-12); die Browsersitzung erreicht ihn nicht, no-store verhindert Zwischenspeicher. Die aufgegebenen /portal/einladung, /portal/kontext, /portal/anhoerung, /portal/zugang-uebernehmen und /portal/teilnahme-beanspruchen führen queryfrei zum festen Loginziel; alte Download-Unterpfade antworten 404. Gastkontext, Gastcookie und Claim werden nicht ausgewertet. /api/caf/documents/shares/* ist hier gesperrt; der CAF-Vertrag anderer Consumer wird nicht verändert. Reverse-Proxy-Zugriffsprotokolle dürfen historische Zugangsqueries nicht aufzeichnen; bestehende Versandhistorie bleibt unverändert.

Neue und noch nicht versandte Nachrichten setzen den tokenfreien Teilnehmer-Tiefenlink erst beim Versand ein. Alte verschlüsselte Outbox-Payloads werden hierfür nicht mehr entschlüsselt. Vertretungskopien vermitteln kein Zugriffsrecht; die Vertretung braucht ihr eigenes gebundenes Konto. Historische Einladungs- und Claim-Datensätze bleiben ausschließlich für Referenzen erhalten.

Seit RAL-QM-HP-20 gibt es neben dem Einzelgrant die Sammelzuordnung: ein bestätigter Auftrag erteilt einer Unterlage je beim Speichern aktiver Beteiligung ohne aktiven Grant einen neuen; zurückgezogene Beteiligte werden übersprungen, bestehende Grants bleiben, ein wiederholter Aufruf erzeugt nichts. Dieselben Wächter wie beim Einzelgrant gelten (Zweckfenster nach RAL-QM-HP-21, aktive aktuelle Version), und der gelesene Aggregatstempel wird vor jeder Änderung geprüft, sodass ein Teilnehmerwechsel zwischen Vorschau und Bestätigung den Auftrag abweist. Die Unterlage merkt sich den Zeitpunkt (GrantedToAllParticipantsAtUtc); eine danach hinzugefügte Beteiligung erhält die Unterlage nicht automatisch – das bleibt die Pflichtunterlage –, sondern wird in der Verfahrensakte benannt. Die Freigabeseite zeigt der Geschäftsführung dieselben Grants je Institution mit Name, Dateityp, Rolle und Zweck, warnt textlich vor einer Institution ohne Unterlage und bietet den Abruf nur über die bestehende autorisierte Download-Route und nur mit Documents.Download; Approve erweitert keine Dokumentberechtigung, Liste und Abruf ändern keine Daten.

Stellungnahme mit Anlagen (RAL-QM-HP-38)

Seit RAL-QM-HP-38 schreibt das Portal die Stellungnahme samt Anlagen in einem Aufruf und in zwei Phasen (HearingAccountPortalAppService.SubmitResponseAsync). Seit RAL-QM-HP-40 erzeugt AppendResponseAsync im selben Ablauf einen neuen Beitrag; AmendResponseAsync weist Änderungen zurück. Das folgende Diagramm zeigt den ursprünglichen atomaren Ablauf aus RAL-QM-HP-38; die dortige Anlagenentfernung ist seit der Festschreibung durch RAL-QM-HP-40 nicht mehr zulässig.

sequenceDiagram
    actor P as Institutionskonto
    participant H as Hearings
    participant T as HearingPortalWriteTransaction
    participant D as Documents-Contract
    P->>H: Abgabe (Kategorie, Text, Dateien, zu entfernende Anlagen)
    H->>H: Vorprüfung ohne Transaktion: Bindung, Beteiligung,
Dispatched, Frist, Kategorie, Anzahl, Regel 2 H->>P: Dateien vollständig lesen (Grenze je Datei + 1 Byte) H->>T: Speicherphase, Serializable, requiresNew T->>H: frische Lesung mit Stempelvergleich H->>D: je Datei CreateAsync (Name, Typ, Signatur, Größe) H->>H: Antwort einfügen (Index), Verlauf, Beteiligungsstand T-->>H: Commit — oder Deadlock-Opfer: bis zu zwei Wiederholungen
mit denselben gelesenen Dateien

Jede Abweisung in der Speicherphase rollt alles zurück, auf dem Datenbank-BLOB-Provider die Dateien eingeschlossen. Wiederholt wird nur das Deadlock-Opfer (Regeln in Querschnitt §2); Stempelkonflikt, abgewiesene Datei und eindeutiger Index werden nicht wiederholt. Seit RAL-QM-HP-40 reserviert die Speicherphase vor der Sequenzvergabe die Teilnahme zum Schreiben. Erstabgabe verlangt eine leere Folge, Ergänzung einen vorhandenen Vorgänger. Eine bereits bekannte Auftragskennung liefert nach den aktuellen Zugangs- und Fensterprüfungen genau ihren Beitrag, wenn Akteur und Inhalt übereinstimmen; sonst folgt ein Operationskonflikt. Die neuen Dateien werden vor der Festschreibung diesem Beitrag zugeordnet. Danach schützen Domäne und Persistenzpfad ihn vor Änderungen und Löschung. Frühere weich gelöschte Anlagenzuordnungen bleiben erhalten und vermitteln keinen Downloadzugang. Nachweise: HearingResponseSubmissionSqlServerTests.

Weiterführend: Sicherheitskonzepte, Bausteinsicht.

Eingangsbestätigung und Referentenhinweis (RAL-QM-HP-35): Jede im Portal festgeschriebene Abgabe – Erstabgabe und jede Ergänzung – legt in derselben Serializable-Speicherphase zwei Aufträge an: die Bestätigung an die operative Kontaktadresse der Beteiligung und den Hinweis an den in diesem Moment zuständigen Referenten. Die Anlagen tragen den SHA-256 der übernommenen Bytes. Ein Worker stellt die Aufträge mindestens einmal zu (Klartext, ohne Stellungnahmetext; tokenfreier Portalverweis bzw. Prüflink), meldet Fehler als Störung, pausiert nach 72 Stunden ohne erreichbaren Empfänger und lässt sich über „Wiederholen“ auf /stoerungen erneut anstoßen oder an den aktuellen Empfänger fortsetzen. Eine zurückgerollte Abgabe hinterlässt keinen Auftrag, ein Replay keinen zweiten.

6.5 Arbeitsablage und Papierkorb (RAL-QM-HP-19)

Filing prüft pro Aufruf die ABP-Permission, die aktuelle aktive interne Kontoprojektion mit genau einer Produktrolle Referent oder Administrator und eindeutiger Keycloak-Bindung sowie Bereichseigentum. Beide Rollen dürfen gemeinsame und eigene persönliche Bereiche verwenden; fremde persönliche Bereiche bleiben auch für Administratoren bei allen Operationen verborgen. Die technische ABP-Rolle admin allein genügt nicht. Der Menüaufbau und der Purge-Worker verwenden dieselbe aktuelle Kontoprüfung. Die administrative Übertragung bleibt auf ein deaktiviertes internes Quellkonto und einen aktiven Referenten als Ziel beschränkt und vermittelt keinen Inhaltszugriff auf den fremden Bereich. Auch das ausführende Konto muss aktuell aktiver, eindeutig gebundener Produktadministrator sein. Service, Menü und Seite verlangen Transfer und Accounts.Invite für die Kontenauswahl. Clientziele sind Filing-Element-IDs; verborgene native Bereichs- und Papierkorbwurzeln werden nicht als Navigationsziele ausgegeben. Die Liste lädt unmittelbare Kinder; Breadcrumbs prüfen höchstens 16 sichtbare Vorfahren. Die Namenssuche filtert nur aktive direkte Kinder des gewählten Ordners im gewählten Bereich. Unterordnerinhalte, andere Bereiche und Papierkorb gehören nicht zu dieser Suche. HasChildFolders in Bereichs-, Ordner- und Element-DTOs wird aus aktiven Unterordnern der bereits autorisierten Bereiche abgeleitet, unabhängig von Namensfilter und Paging.

Jede Mutation schreibt zuerst die concurrency-geschützte Bereichsrevision in derselben SQL-Transaktion. Bei Bereichswechseln erfolgt dies nach stabiler ID-Reihenfolge. Erst danach werden Name, Ziel, Unterbaum und Tiefe geprüft. Die betroffenen Unterbaumeinträge werden vor der Domänenänderung mit EF-Tracking geladen, sodass Bereich und Zustand aller Nachfahren gemeinsam gespeichert werden. Reine Verzeichnislisten bleiben ohne Tracking. Anlegen, Upload und beide Kopierpfade binden Wiederholungen an unveränderliche Auftragsdaten in der bestehenden Filing-Historie: normalisierter Name, Zielordner, Bereich, Quelle und Actor. Nach Umbenennen oder Verschieben wird dieselbe Operation-ID weder für einen geänderten Auftrag akzeptiert noch zur Erzeugung eines Duplikats verwendet. Ältere Historienzeilen ohne Name und Zielordner liefern bei einer Wiederholung einen Operationskonflikt; aktuelle ABP-Metadaten können den ursprünglichen Auftrag nicht beweisen. Papierkorbverschiebungen erhalten die Bytes und legen die betroffene native Wurzel in ein eigenes technisches Verzeichnis; früher separat entfernte Kinder bleiben in ihrem eigenen Löschvorgang. Wiederherstellen prüft aktive Ziele und stellt nur den ausgewählten Unterbaum seines Löschvorgangs wieder her. Die Sammelentfernung bindet jede Wurzel an den ausgewählten Löschvorgang. Vor dem Einzelpurge werden Löschurheber, Löschwurzel und DeletionBatchId erneut geprüft; Wiederherstellen und erneutes Löschen liegt außerhalb dieses Umfangs, auch beim selben Akteur. Der dabei gelesene Eintragsstempel schützt vor Änderungen nach der Prüfung.

Endgültiges Entfernen reserviert zunächst alle Datei-Descriptoren und persistiert Purging zusammen mit Actor und Operation-ID. Aktive CAF-Shares sowie auch historische Hearings- Referenzen blockieren den gesamten Auftrag. Der bestehende ABP-Background-Worker-Mechanismus liest die dauerhaften Aufträge erneut, revalidiert Actor, Rechte und Eigentum und entfernt Dateien sowie anschließend ausschließlich leere Ordner. Erst der gemeinsame Commit von Inhaltsentfernung, Purged und Historie bestätigt den Abschluss. Fehler bleiben als feste Fehlerkategorie am Auftrag; ein später berechtigter Inhaber kann ihn ausdrücklich erneut autorisieren. Reservierte Dateien lassen weder Download noch neue CAF-Shares zu. Eine Share-Erzeugung reserviert den vorhandenen Descriptor als erste Descriptor-Operation durch atomaren EF-Self-Update seines unveränderten ConcurrencyStamp. Erst danach werden Descriptor und Löschreservierung gelesen. Die Schreibsperre bleibt bis zum Commit der Serializable-Unit-of-Work bestehen; ein vorheriger Shared-Read mit späterem Lock-Upgrade ist wegen der Konvertierungsdeadlocks bei parallelen Freigaben ausgeschlossen. Ein eindeutiger gefilterter Index der PurgeStarted-Historie bindet die Operation-ID dauerhaft an eine ausgewählte Wurzel und ihren Bereich. Filing übersetzt ausschließlich die entsprechende SQL-Indexverletzung in den fachlichen Operationskonflikt; der Datenbanktreiber dafür bleibt im Produktmodul, der CAF-Dokumentvertrag bleibt providerneutral.

RAL-QM-HP-65 ergänzt zwei getrennte Auftragsursachen: manuelle Anforderung mit menschlichem Actor und automatisches Fristende mit System ohne erfundenen Benutzer. Die zentrale revisionsgebundene Policy startet deaktiviert mit 90 Tagen. Änderungen serialisieren auf derselben SQL-Zeile wie automatischer Vollzug; eine Erhöhung schützt auch bestehende Systemreservierungen. Manuelles Vorziehen bindet dieselbe Operation und hält ursprüngliche Ursache, gebuchten Termin und neue menschliche Anforderung fest. Der Tick bucht jedes fällige ungeschützte Element einzeln; ein Ordner benötigt seine eigene Fälligkeit und tatsächliche Leere. Geschwister behalten unabhängige Fristen. Restore entfernt nur die Basis der wirklich wiederhergestellten Elemente; erneutes Trash erhält einen neuen Batch. Aktive Freigaben und historische Verfahrensbezüge schützen weiter. Vollständig zwischen Ticks durchlaufene CAF-Sharephasen sind durch Schutzendzeit und eine stabile Revision der gesamten Freigabehistorie erkennbar. Unbekannte historische Endzeiten beginnen frühestens mit einer bestätigten aktuellen Beobachtung neu; Listenlesen datiert keinen Bestand um.

/filing/bevorstehende-entfernungen und /filing/loeschauftraege verwenden dieselbe frisch autorisierte reine Projektion wie der Papierkorb. Filter und Sortierung gelten vor Paging. Nur tatsächlich bestätigte Inhalte zählen dedupliziert zur Gesamtgröße; Ordner-Rollups zählen nicht, Purging zählt bis zum tatsächlichen Vollzug weiter. Unbekannte oder widersprüchliche Mengen ergeben einen unvollständigen Gesamtwert. Tägliche und kumulierte Prognosen enthalten ausschließlich bestätigte ungeschützte termingebundene Inhalte, keine offenen Aufträge und keine bereits erfolgte Freigabe. Die administrative Kapazität unter /administration/speicher misst SQL-Dateien bzw. den konfigurierten Dateisystemdatenträger getrennt von Dokumentbytes, mit Quelle und UTC-Zeit; unbekannter physischer SQL-Datenträgerplatz wird nicht geschätzt.

Vor jeder Dokumentmutation hält die bestehende native ABP-UoW einen externen Generationslease bis nach Commit/Rollback und Dispose (ADR-21). Der aktive Modus koppelt auch die nativen CAF-Schreibports und Hearings-eigene Referenz-/Grantänderungen an diese Schranke. Vor Auftrag und Vollzug prüft ein frischer Inhaltsabgleich unter selber Lease erneut. Lokale Befunde sperren betroffene Löschungen; unbekannter Umfang sperrt den Kontext. Der regelmäßige und manuelle begrenzte Vollabgleich und die exakte Wiederanlauffreigabe sind in Kapitel 8 beschrieben. Der kanonische Betriebsweg rotiert die externe Generation vor Restore: Getting Started §6a.

Entfernte Verfahrensdateien werden durch IHearingDocumentTrashPort zugeliefert. Vor Metadatenzugriff gelten die aktuellen Hearings-Leserechte; fehlende Verfahrensrechte liefern keine Verfahrenszeilen und verhindern nicht den berechtigten Ablagezugriff. Die gemeinsame Liste benennt das Verfahren und bietet einen eigenen Bereichsfilter. Restore sperrt zuerst das Hearing-Aggregate, prüft dessen ConcurrencyStamp, Zweckfenster und Namenskonflikte und reaktiviert keine Teilnehmergrants. Freigabe- und Abschlussstände werden nicht umgangen.

Bei geschlossenem ursprünglichem Zweckfenster wählt der Benutzer ausdrücklich „Kopie in Ablage wiederherstellen“ und einen aktiven Ordner seiner persönlichen oder der gemeinsamen Ablage. Filing prüft Read/Manage, Kontoeignung, Zielstempel, freien Namen und Ordnertiefe und sperrt zuerst den Zielbereich. PrepareCopyToFilingAsync prüft die Hearings-Lese- und Downloadrechte, den Verfahrensstempel und die zurückgezogene, noch verfügbare Quelle ohne Löschreservierung. Die Hearing-Schreibsperre und der frische Descriptor-Lesezugriff bleiben in derselben Serializable-Transaktion bis zum Abschluss der CAF-Kopie erhalten. Die Kopie erhält eigene Bytes, Descriptor-ID, Filing-Zuordnung und Historie; Verfahrensreferenz, Originalinhalt, Verfahrensstatus und Grants bleiben unverändert. Die Sperre erneuert lediglich den ConcurrencyStamp des Hearings. Die Operation-ID bindet Quelle, Ziel, Name und Actor. Ein identischer Wiederholungsaufruf prüft aktuelle Rechte und die vorhandene aktive Zielkopie; er benötigt keine erneute Quellkopie und bleibt auch nach späterem Quell-Purge wiederholbar. Die Oberfläche bestätigt ausdrücklich die Kopie, nicht eine Reaktivierung im Verfahren.

Ein expliziter Verfahrens-Purge benötigt Completed, eine entfernte ungeschützte Upload-/Archive- Datei und keine weitere Datei-/Antwortreferenz oder aktive Freigabe. Er nimmt nur die genau betroffene entfernte Hearing-Referenz aus der konservativen Schutzprüfung aus. Dateien mit nativer Ordnerzuordnung bleiben geschützt, damit ältere gemeinsame Verweise auf Ablagebytes nicht gelöscht werden. Cancelled ist keine Freigabe zum endgültigen Entfernen. Hearings persistiert Auftrag, Actor und Retryzustand am eigenen Dokument; der Worker verwendet die CAF-Reservierung und prüft Kontoeignung sowie Filing-/Hearings-Permissions erneut. Der erhaltene Dokumentdatensatz unterscheidet beabsichtigtes Purged von beschädigtem Inhalt.

Der aktuelle Filing-Upload-/Kopierpfad einschließlich dieser Wiederherstellungskopie verlangt den Datenbank-BLOB-Provider, weil dessen Inhalt mit SQL zurückgerollt werden kann. Seit RAL-QM-HP-81 entfernt zwar ein Abgleichslauf die Reste gescheiterter Erstellungen auf dem Dateisystem, die Sperre bleibt aber: Eine Löschung ist dort sofort endgültig, und ein Rollback ihrer Unit of Work ließe einen Deskriptor ohne Inhalt zurück (Documents-Vertrag); ein Best-Effort-Catch ist keine Zusicherung. Die Oberfläche konsumiert Cohesion über den gleitenden Bereich 3.*-* — eine Version steht hier bewusst nicht mehr, sie wäre bei jedem Paketstand aufs Neue falsch (siehe Directory.Packages.props). Zugesichert wird das Verhalten: Der Explorer bestätigt Navigation erst nach der Serviceantwort und ignoriert veraltete Antworten. Seit 3.6.3-alpha.0.6 sind Krümelpfad und Mehrfachauswahl des CohesionDataBrowser Opt-in (EnableBreadcrumbs, EnableSelection, Vorgabe jeweils false); die drei Aufrufstellen im Produkt setzen sie ausdrücklich, sonst verschwände der Standortpfad lautlos. Der Baum lädt direkte Ordnerkinder beim Aufklappen unabhängig von der aktuell gefilterten Dateilistenseite. Refresh veröffentlicht neue Wurzeln und Standortinhalt gemeinsam; Knotenupdates bewahren die feste Bereichsreihenfolge. Ein letzter Blattknoten öffnet die Papierkorbroute und ist kein auswählbares Schreibziel. Die Ordneranlage verwendet einen modalen Cohesion-Dialog; Dateiaktionen einen zentrierten Icon-Button und bis zur integrierten Tastatur-/Fokusprüfung des korrigierten RCL-Menüs einen Dialog. Standardmäßig erscheinen 25 Einträge; die Oberfläche zeigt Anzahl und Auswahl 25/50/100 auch bei nur einer Seite. Die Suchbeschriftung benennt den Umfang, Navigation löscht den Filter. Uploaddateien erhalten Einzelresultate und unabhängige Operation-IDs; Namenskonflikte verlangen Überspringen oder einen bestätigten Namen. Änderungen senden die gelesenen Bereichs-/Elementstempel. Papierkorb und Transfer verlangen eigene Bestätigungen, Purge zusätzlich die separate Permission. Die flache Papierkorbliste verwendet eine semantische Tabelle mit Cohesion-Aktionsbuttons. Der Clipboard-ARIA-Fix liegt in der RCL vor; ein erneuter Grid-Einsatz verlangt den realen Clipboard-/axe-Nachweis (CAF-Override-Register). Der vollständige Portal-Browserlauf vor der Adminerweiterung ist in Gesamtgate 5 vom 2026-09-10 grün. Die Adminerweiterung wurde am 2026-09-11 in SQL und mehrfach in der gesamten Filing-Browserstrecke einschließlich Transfer, Tastatur, Mobilansicht und axe geprüft. Der aktuelle Reviewstand ist in ral-qm-hp-19-review-gate-5 am 2026-09-11 um 14:01:52 MESZ vollständig grün: 3.204 Tests ohne Skips, kein EF-Modellunterschied und der ungefilterte Portal-Browserlauf einschließlich Migration/Seed aus leerer Datenbank und Neustart. Die Spec führt den aktuellen Nachweis getrennt von den historischen Fehlerbildern. Der Verfahrenspapierkorb einschließlich Kopie bei geschlossenem Ursprungsfenster ist im integrierten Stand von RAL-QM-HP-21 implementiert; die zugehörige aktuelle Ausführungsevidenz wird in der Spec getrennt geführt. Die Snapshot-Übernahme in der Gegenrichtung ist mit RAL-QM-HP-16 hinzugekommen und steht in 6.6. Offener Abnahmeteil bleibt allein der externe RAL-Providernachweis.

6.6 Ablagedatei ins Verfahren übernehmen (RAL-QM-HP-16)

Die Gegenrichtung zu 6.5: Eine aktive Datei aus dem gemeinsamen oder dem eigenen persönlichen Bereich wird als eigenständige Verfahrensfassung in ein Verfahren übernommen — eigene DocumentId, eigener Descriptor, eigene Bytes. Orchestriert wird sie in Filing.Runtime (FilingAppService.HearingAdoption.cs), weil die Laufzeitkante nur in dieser Richtung besteht; die Verfahrenshälfte liegt hinter IHearingDocumentAdoptionPort. Ein Aufruf behandelt genau eine Datei mit eigener Operation-ID; die Oberfläche ruft je ausgewählter Datei einmal auf. Ein Serverbatch scheidet aus, weil jede Mutation des Dokumentports die Transaktion des Aufrufers verlangt und ein Fehler bei der dritten Datei die ersten beiden mit zurückrollen würde.

sequenceDiagram
    actor R as Referent (RALQMCaseOfficer)
    box transparent Filing-Orchestrierung
        participant F as FilingAppService
Serializable-UoW je Datei participant Area as Bereich + Element
(RALQMFilingAreas/-Entries) participant U as Verwendungsnachweis
(RALQMFilingHearingUsages) end box transparent Hearings participant P as IHearingDocumentAdoptionPort end participant Doc as IDocumentFilingPort
+ ABP File Management R->>F: AdoptToHearingAsync (eine Datei,
eigene OperationId) F->>F: Rechte gestapelt + Kontoeignung
(EnsureFilingUserAsync) F->>U: OperationId bereits vergeben? alt Wiederholung derselben Bestellung U-->>R: AlreadyApplied, nichts geschrieben else abweichende Bestellung unter derselben Id U-->>R: OperationConflict else erster Lauf F->>Area: Bereich sperren (Bereichsrevision),
Quelle prüfen: aktiv, Datei, Stempel F->>P: PrepareAdoptionAsync — Ziel sperren und
prüfen: Lifecycle, Zweckfenster, Namenskollision P-->>F: bestätigter Zweck, Name,
frischer Verfahrensstempel F->>Doc: CopyDocumentAsync(folderId: null) Doc-->>F: neue DocumentId + Descriptor + Blob F->>P: CompleteAdoptionAsync — anhängen mit
origin = Archive, Verlaufseintrag P-->>F: HearingDocumentId + Übernahmezeit F->>U: Verwendungszeile einfügen F->>Area: Filing-Historie HearingDocumentAdopted F-->>R: Adopted (ein Commit) bzw. Rejected
mit Fehlercode je Datei end
  1. Rechte und Kontoeignung. Es gibt keinen eigenen Permission-Knoten. Der Aufruf stapelt RALQM.Filing.Read mit RALQM.Hearings, RALQM.Hearings.Documents und RALQM.Hearings.Documents.Manage; dazu prüft RequireActorAsync über IStaffFilingAccessPort.EnsureFilingUserAsync die aktuelle Kontoeignung, bevor irgendein Metadatum gelesen wird. Der Port prüft dieselbe Eignung in seiner eigenen Hälfte erneut.
  2. Eine Transaktion je Datei. Die Methode trägt [UnitOfWork(false)] und öffnet eine eigene, transaktionale Serializable-Unit-of-Work. Deadlocks (SQL 1205) werden als AbpDbConcurrencyException gemeldet; von den übrigen SQL-Fehlern wird ausschließlich die Verletzung des namentlich geprüften eindeutigen Index UX_RALQMFilingHearingUsages_OperationId in den fachlichen Operationskonflikt übersetzt — jeder andere Fehler bleibt, was er ist.
  3. Replay-Erkennung. Die committete Verwendungszeile ist der Beleg der Operation. Dieselbe Bestellung unter derselben Operation-ID schreibt nichts erneut und meldet AlreadyApplied; eine abweichende Bestellung (anderes Element, anderes Verfahren, anderer Actor) ist ein Operationskonflikt.
  4. Bereich sperren, dann Quelle prüfen. Die Bereichsrevision wird als erste Schreiboperation gestempelt und arbitriert damit gegen einen gleichzeitigen Purge der Quelle, der denselben Bereich sperrt. Erst danach werden Bereichseigentum, FilingEntry.EnsureActive (Papierkorbzustände → AdoptionSourceMustBeActive), der Elementstempel und FilingHierarchyPolicy.EnsureCopy (nur Dateien, keine Ordner) geprüft.
  5. Ziel sperren und prüfen — vor der Kopie. PrepareAdoptionAsync erzwingt die transaktionale Serializable-Unit-of-Work des Aufrufers und prüft in dieser Reihenfolge: Lebenszyklus zuerst (Completed/Cancelled → TargetClosed), dann das Zweckfenster über den einen vorhandenen Wächter Hearing.EnsureDocumentAttachable(purpose), dann Sperre und Stempelrotation des Verfahrens, dann die Namenskollision gegen aktive Unterlagen desselben Verfahrens. Die Lebenszyklusprüfung steht vor dem Zweckfenster, weil IsDocumentWindowOpen in einem abgeschlossenen oder abgebrochenen Verfahren für alle Zwecke false liefert — ohne die Vorabprüfung wäre „nimmt überhaupt nichts mehr auf“ von „nicht in diesem Schritt“ nicht unterscheidbar. Geschrieben wird hier nichts außer der Sperre.
  6. Warum die Zielprüfung vor der Byte-Kopie steht. Genau dafür sind Prepare und Complete getrennt: Eine abgewiesene Übernahme darf keinen Inhalt hinterlassen, den jemand kompensieren müsste. Erst wenn das Ziel unter seiner Sperre bewiesen ist, entsteht über IDocumentFilingPort.CopyDocumentAsync eine neue DocumentId mit eigenem Descriptor und eigenem Blob. Der Port verlangt dafür die Transaktion des Aufrufers (RequireTransaction/EnsureTransactionalContentMutation), also bleibt die Kopie bis zum gemeinsamen Commit rollback-fähig; ein Kompensationsaufruf wie im Upload-Weg von RAL-QM-HP-21 wird hier nicht gebraucht. Der Preis ist die bestehende Bindung an den Datenbank-BLOB-Provider aus RAL-QM-HP-19 — ein nichttransaktionaler Provider wird abgewiesen, nicht stillschweigend geduldet.
  7. folderId: null ist zwingend. HearingDocumentTrashRetention schützt jeden Descriptor mit Ordnerzuordnung vor dem endgültigen Entfernen. Eine Kopie, die in einem Ablageordner läge, wäre vom Verfahren nie mehr purgefähig.
  8. Anhängen und Verlauf. CompleteAdoptionAsync prüft den frischen Verfahrensstempel und hängt die bereits kopierte Fassung über Hearing.AttachDocument mit origin: HearingDocumentOrigin.Archive an — der erste Erzeuger dieses seit RAL-QM-HP-21 vorhandenen Herkunftswerts. Der Verlaufseintrag DocumentAdoptedFromFiling trägt flach und ohne Klartext nur {"sourceEntryId":"…"}; Ablagename und Pfad erscheinen dort nie (PII-Regel des Verlaufs). Der Übernahmezeitpunkt ist der Anlagezeitpunkt der Verfahrensfassung, derselbe Wert, den HearingDocumentDto als UploadedAtUtc veröffentlicht.
  9. Verwendungsnachweis und Commit. FilingHearingUsage bindet Quell-Element, gelesene Quellfassung, Verfahren, Verfahrensdokument, Übernahmezeit, Actor und Operation-ID; die Zeile wird nie gelöscht. Danach die Filing-Historie HearingDocumentAdopted, dann der gemeinsame Commit von Kopie, Verfahrensfassung, Verlauf, Nachweis und Historie.
  10. Eine fachliche Abweisung ist ein Ergebnis, keine Ausnahme. Sie kommt als FilingAdoptToHearingResultDto mit Status = Rejected und Fehlercode zurück, damit die Schleife über mehrere Dateien weiterläuft; die Transaktion ist zu diesem Zeitpunkt bereits zurückgerollt. Als Ausnahme bleiben: fehlende Berechtigung, fehlende Kontoeignung, EntryNotFound und Deadlock. Die Codes der Hearings- und der Uploadhälfte werden an der Modulgrenze in die sprechenden Filing-Codes übersetzt (AdoptionTargetClosed, AdoptionPurposeNotOpen, AdoptionNameInUse, AdoptionContentRejected) — Vorbild ist HearingTrashCommandAsync. Die Kopie durchläuft die Uploadpipeline erneut: Eine vor RAL-QM-HP-21 abgelegte Datei mit unpassender Signatur wird deshalb sichtbar abgewiesen, nicht still übernommen.
  11. Der Verwendungsnachweis legt nichts Fremdes offen. GetHearingUsageAsync ist mengenbasiert (eine Abfrage für viele Elemente, damit der Warnhinweis vor dem Löschen eines Ordners und später das Leeren des Papierkorbs dieselbe Quelle nutzen). Er lässt zwei Arten von Zeilen weg, ohne zu scheitern: Elemente, deren Ablagebereich der Aufrufer nicht sehen darf — ein fremder persönlicher Bereich bleibt auch hier privat —, und Verfahren, die er nicht lesen darf. Kein Platzhalter verrät, dass dort etwas war. Die verbleibenden Zeilen sind nach Übernahmezeit absteigend sortiert und kennzeichnen Completed und Cancelled als beendet (IsClosed); das Archivkennzeichen des Verfahrens (RAL-QM-HP-44) erreicht die Ablage nicht, weil es weder Schreibschutz noch Retention der übernommenen Datei berührt.
  12. Die Quelle bleibt frei. Umbenennen, Verschieben, Ersetzen, Papierkorb und endgültiges Entfernen der Ablagedatei wirken nicht auf eine bereits übernommene Fassung und werden durch eine Übernahme auch nicht gesperrt. Der Hinweis vor dem Löschen ist ein Hinweis, keine Sperre; der bestehende PurgeProtected-Pfad für echte Descriptor-Mitbenutzung bleibt unberührt.

Anmeldung und Sitzungsablauf (RAL-QM-HP-52 A)

  1. Eine geschützte lokale Adresse wird samt Query durch Shell und ABP-Account-Filter geprüft und über die unveränderte OIDC-State-Runde erhalten.
  2. Nach erfolgreichem Login legt CAF einen serverseitigen Sitzungszustand an. Ein Cookie verweist mit opaker ID darauf; das Refresh-Token bleibt im Prozessspeicher.
  3. HTTP-Navigation und antiforgerygeschützte Bedienmeldungen verschieben nur den Inaktivitätszeitpunkt. Provider-Refresh und Blazor-Keepalive verschieben ihn nicht.
  4. HTTP-Middleware und CircuitHandler prüfen vor Fachcode. Bei Ablauf wird eine neue Anmeldung verlangt; der verworfene Schreibvorgang wird nicht wiederholt.
  5. Abmelden widerruft zuerst lokal für alle Tabs und startet dann OIDC-Sign-out. Ein neues Login verlangt Credentials, auch wenn beim Provider noch SSO besteht. Löscht eine gleichzeitige Anfrage derselben Sitzung deren ABP-Sitzungszeile zuerst, endet die Abmeldung trotzdem mit dem Providerredirect statt mit HTTP 500 (RAL-QM-HP-92).

Die absoluten Grenzen, Konfiguration und Einschränkungen stehen in Kapitel 8.

07. Verteilungssicht

1. Überblick

CAF.Applications.RAL.QualityMarks.HearingPortal (RALQM, "RAL Gütezeichen Anhörungsportal") läuft lokal als Zweiknoten-Deployment: ein nativer .NET-Prozess (der Blazor-Server-Host) und eine Docker-Compose-Infrastruktur mit SQL Server, Keycloak und Mailpit. Es gibt keine separate Web-/API-/Worker-Trennung — Host, Application Services, Blazor-UI, EF-Migrationen und Hintergrundarbeiter laufen im selben Prozess (siehe Bausteinsicht für die fachliche Modulaufteilung, Kontextabgrenzung für die Nachbarsysteme).

Jede Entwicklungsmaschine benötigt Pester >= 5.5 für die versionierten PowerShell-Tests. setup.cmd installiert das Modul im Benutzerprofil, ohne die Vertrauensstellung von PSGallery zu ändern. Fehlt es weiterhin, schlagen Setup, Doctor und Anwendungs-Preflight fehl. -SkipPester unterbindet nur die Installation. Die vollständigen Voraussetzungen und Installationswege stehen im Schnelleinstieg.

text
┌─────────────────────────────── Windows-Workstation ───────────────────────────────┐
│                                                                                     │
│  dotnet-Prozess (Kestrel, Blazor Interactive Server)                               │
│  CAF.Applications.RAL.QualityMarks.HearingPortal (Host)                            │
│  https://localhost:<APP_PORT>                                                      │
│    - Shell, Contracts, Features/* — drei Fachfeatures (Authorization, Hearings,    │
│      Institutions) und sechs ABP-Ersatz-Features (Identity, Languages,             │
│      TextTemplates, AuditLogs, Gdpr, Settings)                                     │
│    - CAF.Modules.{Authentication, Documents, UI.Blazor}                            │
│    - EF Core (SQL Server Provider), ABP Background Worker                          │
│                                                                                     │
│         │ SQLSERVER_PORT             │ KEYCLOAK_PORT                │ SMTP        │
│         ▼                              ▼                              ▼            │
│  ┌───────────────────────── Docker Compose: backend-Netzwerk ──────────────────┐   │
│  │                                                                              │   │
│  │  sqlserver            keycloak                 mailpit                      │   │
│  │  mssql/server:2022    keycloak:26.7.5           mailpit:v1.31.4              │   │
│  │  DBs: RALQM, keycloak  Realm "ral", start-dev    SMTP-Fänger, In-Memory      │   │
│  │  Volume: sqlserver_data (extern, persistent)                                │   │
│  │                                                                              │   │
│  │  sqlserver-init (init-Container, läuft einmalig)                            │   │
│  │  keycloak-provisioner (init-Container, läuft einmalig)                      │   │
│  │                                                                              │   │
│  └──────────────────────────────────────────────────────────────────────────────┘  │
└─────────────────────────────────────────────────────────────────────────────────────┘

Alle Compose-Ports der Development- und local-Stacks sind ausschließlich an 127.0.0.1 gebunden. Produktionsbetrieb, Backupstrategie und Monitoring werden aus dieser lokalen Development-Umgebung nicht abgeleitet (siehe DEVELOPMENT-GUIDE.md, Abschnitt "Docker-Compose-Umgebung ist ausschließlich für lokale Development-Daten"). Für den Serverbetrieb liegt unter applications/RAL/etc/docker/deploy/ ein eigener, bisher unerprobter Einknoten-Stack im Repository (Abschnitt 3); dessen Caddy bindet 80/443 standardmäßig an 0.0.0.0 (CADDY_HTTP_BIND) und ist damit aus dem Internet erreichbar.

2. Knoten und Erreichbarkeit

Die Anwendung besitzt ihren Betrieb unter applications/RAL/QualityMarks/HearingPortal/etc; Entwicklungsskripte und Wrapper liegen am selben App-Root in tools beziehungsweise direkt daneben. Die bisherigen Root-Wrapper delegieren dorthin. Kundenweit geteilte Keycloak-Dateien und der Infrastruktur-Deploy-Stack liegen unter applications/RAL/etc/docker; der Anwendungs-Deploy-Stack liegt im App-etc. Die Compose-Konfigurationen binden ihre gemeinsamen Dateien über relative Pfade ein.

Gemeinsame lokale ABP-/Syncfusion-Lizenzen liegen am Repository-Root unter secrets/appsettings.local.secrets.json; nur die leere .gitkeep ist versioniert. Setup, Doctor und Lifecycle verwenden diesen Pfad. Der Development-/F5-Host liest ausschliesslich die Lizenzfelder, waehrend Container diese als Prozessumgebung erhalten. Stack-Zugangsdaten und Endpunkte bleiben im eigenen Runtime-Verzeichnis. Jeder Entwickler besitzt einen eigenen Cohesion-Feed-Token in der ignorierten secrets/cohesion-feed.json. Setup registriert ihn unter Windows verschluesselt in der Benutzer-NuGet-Konfiguration; Gate/Lifecycle importieren ihn nur in den Prozess. ABP verwaltet seinen Benutzerfeed ueber den eigenen abp login. ABP-Entwickler- Passwoerter gehoeren nicht in die Landingzone. CI besitzt separate Feedzugaenge. Die gemeinsame Datei wird weder in Images noch in Clean-Room-/Graph-Manifeste aufgenommen.

Neue lokale Stacks werden nach CAF-REPO-03 je App, Worktree, Branch und Betriebsart isoliert. Bestehende Legacy-Ressourcen bleiben unangetastet. Der Descriptor unter %LOCALAPPDATA%/CAF/stacks/<worktree-id>/<stack-id> besitzt Namen, reservierte Ports und Docker-Daemon-Zuordnung; settings.env im selben Verzeichnis besitzt lokale Secrets. Container, Netzwerk, Volume, PID-/Status-/Logdateien und Image-Tag sind kontextgebunden. Der Host verwendet fuer F5 denselben lesenden Resolver wie die Lifecycle-Skripte. Das Launch-Profil setzt externalUrlConfiguration: true, damit Visual Studio keine eigenen Standard-URLs injiziert. Widersprechende Endpunkte bleiben ein Startfehler. status.cmd zeigt die tatsaechlichen Endpunkte. Die Portnamen in der Uebersicht beziehen sich auf die Zuteilung im Descriptor.

Das Hearing Portal verwendet weiterhin den bestehenden Realm ral. EnvironmentalLabel ContractManager und LL ContractManager sollen dieselbe Keycloak-Instanz nutzen, erhalten aber auf Kundenvorgabe je einen eigenen Realm und OIDC-Client. Für LogoLicense ist der Realm ral-logolicense mit dem Client ral-logolicense-contractmanager im eigenen lokalen Entwicklungsstack des ContractManagers eingerichtet; die Provisionierung bei RAL ist offen. Der EnvironmentalLabel-Realm ist noch nicht eingerichtet; siehe RAL-Anwendungen.

App-URL, SQL, OIDC/Callbacks, Portal-/Dokumentlinks und SMTP werden aus demselben Kontext abgeleitet. Widersprechende Prozess-/CLI-Endpunkte werden beim Development-Host abgewiesen. Build-/Testbefehle koordinieren die Ausgaben je Worktree. Fuer manuelle parallele Browser-Sitzungen getrennte Profile verwenden; BrowserGate erzeugt eigene Playwright-Kontexte und einen kompletten eigenen Run-Stack.

3. Docker-Compose-Dienste

Definiert in applications/RAL/QualityMarks/HearingPortal/etc/docker/development/docker-compose.yml (Compose-Projekt <stack-id>):

Dienst Image Rolle Lebenszyklus
sqlserver mcr.microsoft.com/mssql/server:2022-CU27-ubuntu-22.04 (Digest-gepinnt) Persistenter SQL-Server-Provider für Anwendung und Keycloak restart: unless-stopped, Healthcheck via sqlcmd
sqlserver-init dasselbe SQL-Server-Image Einmaliger Init-Container: legt RALQM-Login (RALQM-login.sql) und keycloak-DB (keycloak-database.sql) an restart: "no", läuft nach sqlserver healthy, terminiert nach Erfolg
keycloak quay.io/keycloak/keycloak:26.7.5 (Digest-gepinnt) OIDC-Provider, start-dev --import-realm, persistiert im mssql-Backend (DB keycloak) restart: unless-stopped, Healthcheck gegen /health/ready
keycloak-provisioner dasselbe Keycloak-Image Einmaliger Init-Container: reconciliiert Realm/Client/Mapper/Gruppen/Development-Benutzer/SMTP idempotent (Entrypoint reconcile.sh, siehe „Keycloak-Reconciler“ unten) restart: "no", läuft nach keycloak healthy
mailpit axllent/mailpit:v1.31.4 (Digest-gepinnt; ersetzt MailHog seit 08.10.2026) Development-SMTP-Fänger mit Web-UI und REST-API v1, Nachrichten nur im Container, keine Volumes/Credentials restart: unless-stopped, Healthcheck mailpit readyz
greenmail greenmail/standalone:2.1.14 (Digest-gepinnt), nur mit Compose-Profil delivery-reports (DELIVERY_REPORTS_ENABLED=true) Rückläuferpostfach (RAL-QM-HP-76, ADR-17): SMTP 3025 zum Einliefern von Testmeldungen, IMAP 3143 für den Abruf; Host-Ports GREENMAIL_SMTP_PORT/GREENMAIL_IMAP_PORT; nur Development/Local restart: unless-stopped, Healthcheck TCP auf 3143
clamav clamav/clamav:1.4 (Digest-gepinnt) Schadsoftwareprüfung hochgeladener Dateien (RAL-QM-HP-50, ADR-16), clamd INSTREAM auf 3310, Host-Port CLAMAV_PORT restart: unless-stopped, Healthcheck clamdcheck.sh, start_period 180 s für das Laden der Signaturen

Alle Dienste hängen am Bridge-Netzwerk <stack-id>-backend und schreiben begrenztes JSON-File-Logging (max-size: 10m, max-file: 3). Die kausal abhängige Kette SQL → Principal → Keycloak → Reconcile → Migration läuft laut HearingPortal-Leitfaden §4 nie parallel; lediglich Keycloak, Mailpit und ClamAV starten beim Kaltstart gleichzeitig.

Keycloak-Reconciler: Realm-Scope und Client-Scope

Der Reconciler unter applications/RAL/etc/docker/shared/keycloak/init/ trennt den Realm-Scope des Kundenbetriebs vom Client-Scope der Anwendung. Heute ist dieser Pfad für das Hearing Portal im Realm ral eingerichtet. SMTP, Passwortrichtlinie, Brute-Force-Schutz und Lifespans gehören zum jeweiligen Realm; ein Client-Update darf diese Einstellungen nicht überschreiben. Die zusätzlichen, getrennten Realms für EnvironmentalLabel ContractManager und LL ContractManager benötigen später eigene Provisionierung und passende Prüfungen; die vorhandene HearingPortal-Konfiguration wird dafür nicht unverändert auf weitere Anwendungen übertragen.

Datei Besitzer Inhalt
reconcile.sh Entrypoint beider Compose-Stacks Dünner Orchestrator: führt erst den Realm-, dann den Client-Scope aus
reconcile-common.sh geteilte Bibliothek kcadm-Wrapper, Login, Lookups, ensure_group, Pflichtvariablenprüfung, gemeinsame Konfiguration (Server-URL, Realm, kcadm-Konfigurationen)
reconcile-realm.sh Kunde, genau einmal je Realm Realm-Einstellungen, SMTP, Passwortrichtlinie, Brute-Force-Schutz, Lifespans, Basisgruppen caf-developers und caf-users
reconcile-client.sh Anwendung, je Anwendung einmal Client ral-qualitymarks samt Protocol-Mappern, dedizierter Provisioning-Client, Anwendungsgruppen gz-referenten/gz-geschaeftsfuehrung/gz-institutionen, Development-Benutzer sowie die reine Live-Verifikation (KEYCLOAK_RECONCILE_VERIFY_ONLY)

Lokal ändert sich dadurch nichts: Der Container-Entrypoint bleibt /opt/caf/keycloak-init/reconcile.sh, und ein Lauf konvergiert weiterhin vollständig. In einer geteilten Umgebung gleicht der Kunde den Realm-Scope einmal ab; jede Anwendung führt danach nur ihren eigenen Client-Scope aus und fasst den Realm nicht an.

Drei Schalter machen denselben Reconciler deploy-fähig. Jeder Default reproduziert exakt das bisherige Development-Verhalten:

Variable Default (Development/local) Deploy
KEYCLOAK_SSL_REQUIRED none external für einen aus dem Internet erreichbaren Realm
KEYCLOAK_PROVISION_DEV_USERS true false: keine Development-Benutzer; die beiden Development-Passwörter sind dann auch keine Pflichtvariablen mehr
KEYCLOAK_SMTP_SERVER_JSON Mailpit-Literal (mailpit:1025, ohne Zugangsdaten) Definition eines echten Relays

Die normativen Literale (SMTP-Block, Passwortrichtlinie, Gruppen, Client-Konfiguration) bleiben gegen ral-realm.json durch KeycloakConfigurationConsistencyTests.cs abgesichert; die Tests lesen jeweils die Datei, die das Literal nach der Trennung besitzt.

Persistentes Volume

Genau ein externes, benanntes Volume trägt den gesamten Datenbankzustand:

yaml
volumes:
  sqlserver_data:
    name: ral-qualitymarks-hearing-portal-sqlserver-data
    external: true

external: true bedeutet: Compose legt es nicht implizit an — die Lifecycle-Skripte (start.cmd/setup.cmd) erzeugen und besitzen es explizit. Es überlebt stop.cmd und restart.cmd. Der einzige destruktive Standardpfad ist reset.cmd, strikt begrenzt auf ENVIRONMENT_NAME=Development und exakt dieses Volume (siehe GETTING-STARTED.md, Abschnitt 5).

.env-gesteuerte Konfiguration

Die einzige lokale, nicht versionierte Compose-Konfigurationsdatei ist settings.env im isolierten Stack-Verzeichnis (Vorlage: …/development/.env.example). Sie legt Image-Digests, Ports, SQLSERVER_SA_PASSWORD, RALQM_DB_PASSWORD, KEYCLOAK_ADMIN_PASSWORD, KEYCLOAK_DB_PASSWORD, KEYCLOAK_CLIENT_SECRET, KEYCLOAK_PROVISIONING_CLIENT_SECRET, KEYCLOAK_DEV_ADMIN_PASSWORD, KEYCLOAK_DEV_USER_PASSWORD, APP_URL und TIME_ZONE fest. setup.cmd erzeugt neue Dateien mit den Descriptor-Endpunkten und zufaelligen lokalen Zugangsdaten. Vorhandene Dateien werden validiert; fehlende Werte werden nicht still ersetzt. Secrets erscheinen nie im Repository; Diagnosewege (doctor.cmd) melden nur SET/MISSING (siehe Abschnitt 13 im DEVELOPMENT-GUIDE.md und Querschnittskonzepte für die vollständige Secret-Regel).

Ablage unter etc/docker

Die Compose-Artefakte sind je Kunde, Produkt und Anwendung abgelegt; kundenweit geteilte Bausteine liegen genau einmal unter shared:

text
applications/RAL/
├── etc/docker/
│   ├── shared/keycloak/         # kundenweite Realm-/Reconciler-Quellen
│   └── deploy/                 # Kunden-Infrastruktur und Server-Skripte
└── QualityMarks/HearingPortal/etc/docker/
    ├── development/            # Compose und wertfreie Konfigurationsvorlage
    ├── local/                  # containerisierter Local-Stack und Vorlage
    ├── sqlserver/              # app-eigene SQL-Provisionierung
    └── deploy/                 # Anwendungs-Tier fuer den Serverbetrieb

applications/RAL/etc/docker/shared/keycloak ist die einzige Quelle für Realm-Import und Reconciliation; die Compose-Files der Anwendung binden Keycloak- und SQL-Server-Verzeichnisse relativ ein. local ist der vollständig containerisierte Local-Stack: Er startet neben der Infrastruktur auch Migration und Anwendung als Container aus dem lokal gebauten Image caf/ral-qualitymarks-hearing-portal:<stack-id> (tools/build/build-images-locally.ps1) und läuft mit ASPNETCORE_ENVIRONMENT=Development konfigurationsgleich zum nativen Development-Weg. Eigener Compose-Projektname je App/Worktree/Branch/Local fuer Container, Netzwerk und Volume sowie kollisionsfreie, an Loopback gebundene Host-Ports erlauben den Parallelbetrieb neben dem Development-Stack; konfiguriert wird er über eine eigene, nicht versionierte settings.env im Stack-Verzeichnis (Vorlage local/.env.example, siehe GETTING-STARTED.md, Abschnitt „Containerisierter local-Stack“). Die getrennten Deploy-Quellen tragen den Serverbetrieb (nächster Abschnitt). Vorhandene Legacy-Ressourcen bleiben erhalten; neue lokale Kontexte erhalten eigene Namen.

Deploy-Stack: Kunden-Tier und Anwendungs-Tier

Noch nicht in Betrieb. Die Artefakte unter applications/RAL/etc/docker/deploy/ sind vollständig, aber bisher weder auf einem Kundenserver noch in einer Pipeline gelaufen. Die Development- und local-Stacks bleiben davon unberührt; sie sind weiterhin die einzigen erprobten Wege.

Anders als Development und local ist der Deploy-Stack nicht ein Stack je Anwendung, sondern zwei Ebenen mit unterschiedlichem Besitzer:

Ebene Datei Compose-Projekt Dienste
Kunden-Tier deploy/infra/docker-compose.yml ral-infra sqlserver, sqlserver-init, keycloak (Modus start), keycloak-realm-reconciler, caddy
Anwendungs-Tier deploy/apps/hearingportal/docker-compose.yml ral-hearingportal keycloak-client-reconciler, hearingportal, dazu im Profil ops hearingportal-migrator und sqlserver-reconcile

Der Schnitt folgt dem Besitz und spiegelt exakt die Trennung des Keycloak-Reconcilers (Abschnitt „Keycloak-Reconciler“ oben): In Produktion bedienen ein Keycloak und ein SQL Server die vorgesehenen RAL-Anwendungen des Kunden, während jede Anwendung ihrem eigenen Release-Takt folgt. Die zusätzlichen Realms und Anwendungs-Tiers der beiden weiteren Anwendungen (LogoLicense lokal ausführbar, EnvironmentalLabel Stub) sind im Deployment noch nicht umgesetzt. Ein Anwendungs-Update darf die Identitäts- und Datenbankebene nicht anfassen — deshalb reconciliiert der Kunden-Tier ausschließlich den Realm (reconcile-realm.sh) und der Anwendungs-Tier ausschließlich seinen Client (reconcile-client.sh).

Verbunden werden beide Ebenen über das Bridge-Netzwerk ral-backend: Der Kunden-Tier deklariert es, der Anwendungs-Tier bindet es als external: true ein. Fehlt der Kunden-Tier, bricht der Anwendungs-Tier sofort ab („network ral-backend declared as external, but could not be found“). Beide Ebenen lassen sich unabhängig voneinander starten, stoppen und aktualisieren.

text
┌──────────────── Server (ein Knoten) ────────────────────────────────────┐
│                                                                          │
│  Internet ──► caddy  (nur 80/443, TLS-Terminierung, WebSocket-fähig)     │
│                 │  app.<DOMAIN> ─────────────┐                           │
│                 └─ auth.<DOMAIN> ──┐         │                           │
│                                    ▼         ▼                           │
│  ┌── ral-infra (Kunde) ──────┐   ┌── ral-hearingportal (Anwendung) ──┐   │
│  │  keycloak (start)         │   │  hearingportal (Production)       │   │
│  │  keycloak-realm-reconciler│   │  keycloak-client-reconciler       │   │
│  │  sqlserver + init         │   │  [ops] migrator, sqlserver-reconc.│   │
│  └───────────────────────────┘   └───────────────────────────────────┘   │
│              └────────── Netzwerk ral-backend ──────────┘                │
└──────────────────────────────────────────────────────────────────────────┘

Vier Festlegungen unterscheiden den Deploy-Stack inhaltlich vom local-Stack:

  • Kein Realm-Import. keycloak startet ohne --import-realm und ohne Mount von shared/keycloak/realm/. ral-realm.json ist ein Development-Bootstrap: sieben Development-Benutzer und sslRequired=none gehören nicht auf einen aus dem Internet erreichbaren Server. Den Realm legt stattdessen der Realm-Reconciler an — er ist idempotent und setzt dieselben normativen Werte (abgesichert durch KeycloakConfigurationConsistencyTests.cs).
  • ASPNETCORE_ENVIRONMENT=Production, kein ~/.abp-Mount. Die ABP-Pro-Lizenz wird offline über AbpLicenseCode geprüft statt über den abp.io-Login des Entwicklers. Production aktiviert zugleich UseHsts/UseHttpsRedirection; da im Quellcode keine Behandlung von Weiterleitungs-Headern existiert, setzt der Stack ASPNETCORE_FORWARDEDHEADERS_ENABLED=true — sonst entsteht hinter Caddy eine Endlosschleife aus Weiterleitungen.
  • Migration nur auf ausdrückliche Anweisung. hearingportal-migrator (und der nachgelagerte sqlserver-reconcile, der den [ralqm]-Datenbankbenutzer erst nach dem Anlegen der Datenbank erzeugen kann) liegen im Compose-Profil ops. Ein einfaches docker compose up kann damit keine Migration auslösen, auch nicht beim Neustart des Servers.
  • Persistente Data-Protection-Schlüssel. Der Anwendungs-Tier hängt dafür ein benanntes Volume ein; ohne dieses würde jeder Neustart sämtliche Anmelde-Cookies entwerten.

Dazu kommt die Serverebene selbst: deploy/server/ enthält bootstrap.sh (einmalige Vorbereitung der Maschine), deploy.sh (Routine-Deployment des Anwendungs-Tiers) und die beiden systemd-Units, die die Tiers nach einem Serverneustart wieder hochfahren. Betrieb, Reihenfolge der Inbetriebnahme, Datensicherung und die Liste der noch nicht automatisierten Punkte stehen in applications/RAL/etc/docker/deploy/README.md.

4. Der Host-Prozess

Der ausführbare Knoten ist ein einzelner .NET-Prozess: das Projekt applications/RAL/QualityMarks/HearingPortal/src/Host/CAF.Applications.RAL.QualityMarks.HearingPortal.csproj, im Development-Weg nativ auf Windows gestartet, im local- und im Deploy-Stack als Container aus Host/Dockerfile (Dienst hearingportal). Er bündelt in einem Prozess:

  • Kestrel-Endpunkt mit Blazor Interactive Server Rendering (kein WASM-Bootstrap),
  • die Composition Root, die Shell, Contracts und alle Features/*- sowie CAF.Modules.*-Bausteine referenziert (siehe Bausteinsicht), entsprechend der No-Layer-Architektur aus dem Root-AGENTS.md,
  • den EF-Core-DbContext samt Migrations-Host (applications/RAL/QualityMarks/HearingPortal/src/Host/Migrations),
  • ABP Background Worker für Postausgang/Fristenaktionen (Outbox, Due Actions).

Konfigurationsquellen werden in fester Priorität zusammengeführt (siehe Getting Started, Host-Konfigurationspriorität):

text
versionierte appsettings-Dateien
  < stack-eigene settings.env (nur Development-Workstation)
  < appsettings.local.secrets.json
  < Umgebungsvariablen
  < Kommandozeilenargumente

Host/Configuration/DevelopmentDockerEnvironmentConfiguration.cs liest dafür nur in IsDevelopment() die Datei settings.env im durch den gemeinsamen Resolver gefundenen Stack-Verzeichnis ausserhalb des Repositorys und leitet daraus App:SelfUrl, App:RedirectAllowedUrls, ConnectionStrings:Default (Server 127.0.0.1,<SQLSERVER_PORT>, Datenbank RALQM, Login ralqm), Authentication:Authority (<KEYCLOAK_PUBLIC_URL>/realms/ral) sowie die Keycloak-Provisioning-Werte ab. Damit liest F5 aus Visual Studio dieselbe Laufzeitkonfiguration wie start.cmd, ohne einen vorgeschalteten PowerShell-Prozess zu benötigen. Fehlt .env oder ist ein Pflichtwert noch CHANGEME, bricht der Start fail-closed mit einer expliziten Meldung ab.

5. Migration und Seed (--migrate-database)

Schema und Demodaten werden nicht implizit beim normalen Start ausgeführt, sondern über denselben Host-Prozess mit einem expliziten Kommandozeilenschalter:

bash
dotnet run --project applications/RAL/QualityMarks/HearingPortal/src/Host -- --migrate-database

Program.cs erkennt --migrate-database (IsMigrateDatabase), schaltet auf ein Migrations-Logging-Profil um und ruft nach InitializeApplicationAsync() den HearingPortalDbMigrationService auf. Dieser läuft in drei Schritten:

  1. EnsureMigrationsExist() — der HearingPortalMigrationPathGuard sucht ab dem Startverzeichnis nach dem CAF.slnx-Repository-Marker und verlangt, dass applications/RAL/QualityMarks/HearingPortal/src/Host/Migrations existiert und mindestens eine echte Migrationsklasse enthält (kein .Designer.cs, kein ModelSnapshot.cs) — ein Schutz gegen einen falsch aufgesetzten Arbeitsbaum. Findet sich kein Marker (Published-Lauf, z. B. der containerisierte local-Stack), prüft der Guard stattdessen fail-closed, dass mindestens eine kompilierte EF-Core-Migrationsklasse in der Host-Assembly vorhanden ist.
  2. MigrateDatabaseSchemaAsync() — führt die EF-Core-Migrationen über den HearingPortalDbSchemaMigrator gegen die RALQM-Datenbank aus.
  3. SeedDataAsync() — ruft ABPs IDataSeeder mit einem zufällig erzeugten, deaktivierten Bootstrap-Administratorpasswort auf (kein festes Secret im Code).

Praktisch läuft dieser Schritt in den Lifecycle-Skripten mit einem separaten, nur für den Bootstrap privilegierten SQL-Zugang; danach reconciliiert start.cmd den dauerhaften, eingeschränkten ralqm-Datenbankprincipal (siehe GETTING-STARTED.md, Abschnitt 6, Schritt 9). Migration und Seed sind idempotent: ein wiederholter Lauf legt keine Duplikate an und überschreibt keine vorhandenen Demodaten (setup.cmd -WithDemoData erzwingt den Abgleich bewusst erneut).

6. Lokaler Start- und Betriebsweg

Der schlanke, skriptlose Pfad — vorausgesetzt settings.env im isolierten Stack-Verzeichnis und appsettings.local.secrets.json liegen bereits vor:

bash
# 1. Infrastruktur (SQL Server, Keycloak, Mailpit)
docker compose -f applications/RAL/QualityMarks/HearingPortal/etc/docker/development/docker-compose.yml up -d

# 2. Schema/Seed (nur beim ersten Start bzw. nach neuen Migrationen)
dotnet run --project applications/RAL/QualityMarks/HearingPortal/src/Host -- --migrate-database

# 3. Anwendung starten
dotnet run --project applications/RAL/QualityMarks/HearingPortal/src/Host

Der komfortable, empfohlene Weg sind die Root-.cmd-Wrapper (setup.cmd, start.cmd, status.cmd, stop.cmd, restart.cmd, reset.cmd, doctor.cmd, logs.cmd), dünne Aufrufer der kanonischen PowerShell-Skripte unter tools/scripts. start.cmd führt deterministisch aus: SDK-/Zertifikatsprüfung → Restore → Docker-/Volume-Prüfung → SQL Server + Init-Container → Keycloak + Mailpit parallel → Keycloak-Reconciliation → Migration/Seed → ralqm-Principal-Reconciliation → Start des Host-Prozesses als versteckter Windows-Prozess (PID-Datei unter %LOCALAPPDATA%\CAF.Applications.RAL.QualityMarks.HearingPortal). Details, Idempotenz-Garantien und alle Kommandovarianten stehen in GETTING-STARTED.md, Abschnitte 4–6.

7. Zuordnung Bausteine → Infrastruktur

Baustein Läuft in Persistiert/kommuniziert über
Features/Authorization, Features/Hearings, Features/Institutions Host-Prozess (in-process) EF Core → SQL Server (RALQM, Tabellenpräfix RALQM…)
ABP-Ersatz-Features Features/{Identity,Languages,TextTemplates,AuditLogs,Gdpr,Settings} Host-Prozess (in-process) ABP-Modulschemata über denselben HearingPortalDbContext → SQL Server (RALQM)
CAF.Modules.Documents(.Contracts) Host-Prozess ABP File Management → SQL Server (RALQM)
CAF.Modules.Authentication Host-Prozess OIDC-Client gegen die stack-eigene Keycloak-URL, Realm ral
CAF.Modules.UI.Blazor Host-Prozess (Blazor Interactive Server) Cohesion-Assets aus wwwroot, kein externer Dienst
Shell, Contracts Host-Prozess keine eigene Persistenz
Hearing-Versand (IEmailSender) Host-Prozess (Background Worker) SMTP → Mailpit (127.0.0.1:1025)
Schadsoftwareprüfung (DocumentScanWorker, Upload) Host-Prozess clamd INSTREAM → ClamAV (127.0.0.1:CLAMAV_PORT, im Local-/Deploy-Stack clamav:3310)
Keycloak-Provisioning (Mitarbeitenden-/Institutionskonten) Host-Prozess Keycloak Admin-REST via dediziertem Service-Client ral-qualitymarks-provisioning
EF-Migrationen Host-Prozess (--migrate-database) SQL Server (RALQM)

Es gibt keine eigenständig deploybaren Knoten je Feature-Modul: die No-Layer-Architektur (Root-AGENTS.md) bündelt alle Fachmodule im selben Host-Prozess. Im Development-Weg sind nur die Infrastrukturdienste (SQL Server, Keycloak, Mailpit) eigene Container; im local- und im Deploy-Stack läuft derselbe Host-Prozess zusätzlich als eigener Container. Mehrknotenbetrieb und Load Balancing sind nicht Bestandteil dieses Repositories — der Deploy-Stack zielt auf genau einen Serverknoten.

8. Siehe auch

  • Bausteinsicht — fachliche und technische Modulaufteilung innerhalb des Host-Prozesses.
  • Laufzeitsicht — Ablauf einzelner Anwendungsfälle über diese Knoten hinweg.
  • Querschnittskonzepte — Secret-Handling, Konfigurationspriorität, Auditierung.
  • GETTING-STARTED.md — vollständiger Einstieg inkl. Lifecycle-Skripten, Diagnose und Kennzahlen.
  • HearingPortal-Leitfaden §4 — konkrete Konfigurationsregeln.
  • CAF-Leitfaden §15 — Nachweise und Qualitätsgate.
  • applications/RAL/QualityMarks/HearingPortal/etc/docker/development/docker-compose.yml — normative Dienstdefinition.

08. Querschnittliche Konzepte

Dieses Kapitel bündelt Konzepte, die mehrere Module gleichermaßen betreffen: Authentifizierung/ Autorisierung, Persistenz, persistierte Kennungen, Lokalisierung, UI-Grenzen, Fehlerbehandlung und Teststrategie. Die Modulgrenzen selbst stehen in Bausteinsicht, der konkrete Ablauf eines Requests in Laufzeitsicht.

Die lokale F5-Konfiguration ruft den gemeinsamen Stack-Resolver ausschließlich über Windows PowerShell im OS-Systemverzeichnis auf. Der Host gibt das feste Repositoryskript mit ArgumentList, UseShellExecute=false, deaktiviertem Profil und ohne interaktive Eingabe weiter; CAF_STACK_ID wird vor dem Start validiert. Weder ausführbare Datei noch Skript oder Command-Text sind Requestparameter. Der Checkout bleibt ausführbarer, vertrauenswürdiger Entwicklercode. Die beiden eng begrenzten SCS0001-1-Ausnahmen und der reale Prozessnachweis sind in CAF-REPO-02, Abschnitt 11 erläutert. Produktive Authentifizierung und ABP-Modulgrenzen ändern sich dadurch nicht.

1. Authentifizierung und Autorisierung

text
Keycloak (OIDC, Authorization Code + PKCE)
  → CAF.Modules.Authentication (Cookie-Session, Claim-Mapping, Revalidierung)
    → lokaler ABP IdentityUser, gebunden über den stabilen OIDC-Subject "sub"
      → RALQM-Rolle (Administrator/CaseOfficer/Management/Institution)
        → ABP Permission (RALQM.<Feature>…)
          → Application Service + Objektprüfung
  • Keycloak ist alleiniger Token-Issuer und Identitätsquelle; die Anwendung betreibt keinen lokalen OpenIddict-Server.
  • Kundendesign (RAL-QM-HP-102, ADR-23): Das gemeinsame RAL-Basistheme ral-v1 erbt keycloak.v2; das app-eigene ralqm-legal erbt ral-v1 und behält Rechtsfooter sowie bestehende A11y-Korrekturen. Im bestehenden Realm ral ist ral-v1 der Standard, damit abgelaufene native Aktionslinks auch ohne Clientkontext das Kundendesign erhalten. Der HP-Clientoverride bleibt ralqm-legal; andere Clients ohne Override erben nur die Kundenbasis. Andere Realms, Credentials, native Provideraktionen und OIDC-Grenzen werden nicht geändert. Vorhandene RAL-/HearingPortal-Assets werden lokal ausgeliefert; Hell/Dunkel folgt der OS-Präferenz ohne neue Speicherung oder Cross-Origin-Übernahme. Umsetzungsentscheidung und Rückfall: ADR-23.
  • CAF.Modules.Authentication ist der wiederverwendbare, anwendungsneutrale OIDC-Client (Authorization Code mit PKCE, Cookie-Session-Policy, Token-Propagierung, revalidierender AuthenticationStateProvider, Sign-out). Er kennt weder GZ-Rollen noch Permissions — siehe modules/Authentication/README.md.
  • ABP Identity speichert das lokale Benutzerabbild. Die Verknüpfung verwendet ausschließlich den stabilen sub, niemals die E-Mail-Adresse.
  • HearingPortal.Authorization definiert die produktweiten Rollen (HearingPortalRoles: RALQMAdministrator, RALQMCaseOfficer, RALQMManagement, RALQMInstitution) und komponiert deren Grants aus den Permissions der Feature-Contracts. Ein neues Feature-Recht wird nicht zentral in Authorization deklariert, sondern im owning {Feature}.Contracts-Projekt.
  • Rollenzuschnitt seit RAL-QM-HP-18: Der Systemadministrator ist eine Verwaltungsrolle ohne Verfahrens- und Freigaberechte — Kontenverwaltung, Rollenvergabe, Institutionsstammdaten, Wertelisten, Ablage, Vorlagen, Postausgang, Störungen und manuelle Fristläufe, aber kein Anlegen, Einreichen, Freigeben, Versenden, Bescheiden oder Lesen von Stellungnahmen. Referent (Fallarbeit inklusive Versand, Nachversand, Bescheidungsübermittlung RALQM.Hearings.Adjudicate und Abschluss RALQM.Hearings.Complete) und Geschäftsführung (Vier-Augen-Entscheide: Freigabe RALQM.Hearings.Approve und Nachversand-Freigabe, dazu Abbruch) halten zusammen jedes Verfahrensrecht. Seit RAL-QM-HP-51 (SPEC-RAL-QM-HP-46 Regel 1/O1, SPEC-RAL-QM-HP-51 O2, RAL-QM-HP-34 AC-2/3) liegen Bescheidungsübermittlung und Abschluss beim Referenten; die Geschäftsführung bescheidet und schließt nicht ab, der Seed entzieht ihr beide Rechte in bestehenden Datenbanken. Direkter und kombinierter einstufiger Abschluss setzen dieselbe Rollenregel durch (Dienst: Adjudicate UND Complete, Oberfläche zusätzlich AuthorizeView auf Complete). Die Rolle Institution trägt nur RALQM.Hearings.Portal.Access. Kontenrechte: RALQM.Accounts (Übersicht), RALQM.Accounts.Invite (interne Konten, Systemadministrator), RALQM.Accounts.InviteInstitution (Institutionskonten, Systemadministrator und Referent). Der Seed gleicht die Grants der vier Produktrollen bei jedem Start exakt ab (HearingPortalDomainRolesDataSeedContributor); die technische ABP-Rolle admin des Ersteinrichtungskontos bleibt außerhalb dieser Komposition.
  • Die Permission-Gruppe heißt RALQM (HearingPortalPermissions.GroupName); Feature-Permissions hängen sich darunter an, zum Beispiel RALQM.Hearings.Approve oder RALQM.Accounts.Invite.
  • Jeder Application Service prüft seine Permission serverseitig; Objektzugriff, Status und Teilnehmerzuordnung werden zusätzlich im owning Feature geprüft. Ein versteckter Button ist keine Autorisierung.

Der gemeinsame Papierkorb kombiniert getrennte Autorisierungsbereiche. Filing prüft Read und aktuelle interne Kontoeignung; Hearings liefert Verfahrenszeilen erst nach seinen eigenen Leserechten. Weder bekannte IDs noch ein gemeinsamer Filter erweitern diese Sicht. Für die Wiederherstellung im ursprünglichen Verfahren gelten Filing.Read/Manage und die Hearings-Lese-/Dokumentpflegerechte sowie das offene Zweckfenster. Ist dieses geschlossen, erfordert die ausdrücklich gewählte unabhängige Ablagekopie Filing.Read/Manage, Hearings.Default/Documents.Default/Download und ein aktuell zulässiges eigenes oder gemeinsames Ziel. Die Quellautorisierung und -sperre bleibt bis zum Zielcommit in derselben Serializable- Transaktion erhalten; bekannte Dokument-IDs oder ein früher erteilter Grant ersetzen sie nicht. Verfahrensstatus, Quellreferenz und widerrufene Teilnehmergrants ändern sich durch die Kopie nicht. Auch ein idempotenter Wiederholungsaufruf prüft aktuelle Rechte und die gebundene aktive Zielkopie. Verfahrens-Purge benötigt zusätzlich Filing.Purge und die Hearings-Lese-/Dokumentpflegerechte. Der Hintergrundprozess baut für den protokollierten Actor einen aktuellen Principal und prüft diese Grants erneut. Kontenübertragung verlangt einen aktuellen Produktadministrator und Transfer sowie Accounts.Invite; die Berechtigung des Actors ist unabhängig von der Eignung des Quell- und Zielkontos. Weitere Abläufe beschreibt Kapitel 6.5.

Die Übernahme einer Ablagedatei ins Verfahren (RAL-QM-HP-16) bekommt keinen eigenen Permission-Knoten, sondern stapelt vorhandene Rechte: RALQM.Filing.Read für die Quelle sowie RALQM.Hearings, RALQM.Hearings.Documents und RALQM.Hearings.Documents.Manage für das Ziel. Wer eine Unterlage anhängen und die Ablage lesen darf, darf fachlich auch übernehmen; ein neues Recht hätte dieselbe Menge beschrieben und zusätzlich vergeben werden müssen. Zu den ABP-Grants tritt wie bei jeder Filing-Operation die Kontoeignung — aktives internes Konto mit genau einer Produktrolle Referent oder Administrator und eindeutiger Keycloak-Bindung (IStaffFilingAccessPort.EnsureFilingUserAsync) —, geprüft vor jedem Metadatenzugriff und in beiden Hälften: im Filing-Dienst und noch einmal im Hearings-Port, der seine eigene Fläche nicht von der Vertrauenswürdigkeit des Aufrufers abhängig macht. Der Verwendungsnachweis verlangt dieselben Leserechte ohne Manage. Er legt nichts Fremdes offen: Zeilen zu Verfahren, die der Aufrufer nicht lesen darf, und Zeilen zu Elementen in einem fremden persönlichen Ablagebereich fallen weg, ohne dass die Abfrage scheitert und ohne Platzhalter, der ihre Existenz verriete — dieselbe Regel wie bei IHearingDocumentTrashPort.GetListAsync. Bezeichnung und Status eines nicht lesbaren Verfahrens verlassen die Hearings-Hälfte gar nicht erst. Eine fachliche Abweisung der Übernahme ist ein Ergebnis mit Fehlercode, keine Ausnahme, damit eine Mehrfachauswahl je Datei weiterläuft; fehlende Berechtigung und fehlende Kontoeignung bleiben Ausnahmen und brechen ab. Ablauf: Kapitel 6.6.

Die Teilnahme-Seitenroute prüft vor dem Rendern denselben aktuellen Lesefilter wie die Portalübersicht. HearingPortalParticipationRouteMiddleware liegt hinter Autorisierung und Rate-Limiter und fragt den prozessinternen Vertrag IsParticipationReadableAsync ab. Fremde, unbekannte und entzogene Beteiligungen antworten neutral mit 404 ohne Fachinhalt. Die gemeinsame Policy hearing-portal begrenzt Teilnahme-Seite und Dokumentdownload; sie ersetzt weder die aktuelle Kontobindung noch den institutionsbezogenen Dokumentgrant. Dieser Vertrag aus RAL-QM-HP-30 verwendet vorhandene Tabellen; seine SQL-Nachweise prüfen Filter und konkurrierende Zugriffe, keine Schemaänderung.

Die Abschlussansicht (RAL-QM-HP-62) zeigt bei einem beendeten Verfahren den Verlauf der eigenen Beteiligung als Projektion, nie den internen Verlauf: HearingPortalTimelineRules nimmt nur die Stationen der Positivliste (Einladung bzw. Nachversand, Erinnerung, eigene Abgabe, übermittelter Bescheid, Abschluss oder Abbruch), Zeitpunkte aus Aggregat und eigenen Antworten und aus dem Verlauf allein die Einträge 220 und 390, zugeordnet über das Subjekt und nie über den Akteur. Der Portaldienst liest diese Einträge erst nach der Autorisierung und nur bei beendetem Verfahren; ausgegeben werden Art, Zeitpunkt und Folge, keine Kennungen, Namen oder DetailJson. Der Abbruchgrund erscheint nur mit amtlicher Kennzeichnung (RAL-QM-HP-42), „Keine Aussage“ nur als Erklärung aus RAL-QM-HP-33.

Interne Verfahrensuploads, Versionsersatz und Downloads prüfen Hearings-Rechte, Fachobjekt, Zustand und beim Schreiben den Stempel, bevor sie IDocumentContentPort aufrufen. Referenten benötigen hierfür keine generischen CAF-Dokumentrechte. Bei einem Fehler nach dem Upload darf die Kompensation über IDocumentFilingPort nur den gerade angelegten Descriptor mit dessen Stempel und eigener Löschoperation entfernen. Allgemeine Dokumentdienste behalten ihre eigenständigen Rechteprüfungen.

Rückmeldungen lesen und beantworten (RAL-QM-HP-36)

Wer Rückmeldungen lesen darf – RALQM.Hearings.Responses und .Read –, erreicht die Rückmeldungsseite über den Prozessschritt „Rückmeldungen auswerten“ und den Verweis im Reiter „Rücklauf“ und liest dort den Beteiligungsstand, alle Abgaben mit Text und Anlagen und die Antworten auf Einwände. Schreiben bleibt bei eigenen Rechten: Antwort verfassen .Reply, intern erfassen .RecordInternally, exportieren RALQM.Hearings.Export. RAL-QM-HP-36 ändert keine Rollenkomposition: Die Geschäftsführung liest und exportiert, verfasst und erfasst aber nicht; der Referent hält alle vier Rechte; der Systemadministrator liest keine Stellungnahme; RALQM.Hearings allein reicht nicht. Die Seite selbst verlangt neben dem Leserecht nur RALQM.Hearings für die Rahmenlesung des Verfahrens; aus ihr stammen auch die Beteiligungen für die Beschriftung und die Erfassungsauswahl, ein Teilnehmerrecht braucht die Seite nicht.

ABP verknüpft die [Authorize]-Attribute von Klasse und Methode mit UND. HearingResponseAppService und HearingDialogAppService tragen deshalb an der Klasse das schwächste Recht des Knotens (Responses) und an jeder Methode ihr eigenes. Bis RAL-QM-HP-36 stand .Reply an der Klasse des Dialogdienstes; damit las die Antworten niemand, der nur lesen durfte.

Leser erfahren, dass eine Zustellung fehlgeschlagen ist – als Stand oder Hinweis, in Zeile und Export. Adresse, Zahl der Versuche und Fehlergrund bleiben beim Zustellstatus hinter RALQM.Hearings.Dispatch. Die Auswertung liest aus dem Postausgang je Zeile Art, Schlüssel, Transportstatus und Erzeugungszeitpunkt und – nur um zu entscheiden – den Versuchszähler, den gemeldeten Versuch und den Verweis auf die ersetzte Zeile (HearingMailFact). Ausgegeben wird davon nichts: Der Vertrag der Übersicht hat kein Feld dafür. Adresse, Fehlercode und Meldegrund liest die Auswertung gar nicht. Nachweise: HearingResponseReplyTests (Form der Rechte, Rollen), die Datei HearingResponseEvaluationSqlServerTests.cs im Projekt Hearings.SqlServer (Teil der Klasse HearingResponseSubmissionSqlServerTests; echter ABP-Proxy auf SQL Server: Geschäftsführung, reines Leserecht, fünf Konten ohne Leserecht) und HearingResponseOverviewBoundaryTests (Vertrag).

Maschinenabruf der öffentlichen Anzeige (RAL-QM-HP-47, ADR-12)

Der Bestandsabruf für die Website ist der einzige eingehende Maschinenzugang. Er nutzt ein eigenes JWT-Bearer-Schema HearingPublicationFeed neben dem interaktiven Cookie, nie als Standardschema: die CAF-Sitzungsprüfung sieht deshalb keinen Bearer-Prinzipal, und die Policy hearing-publication-feed nennt das Schema, sodass die Autorisierung den Aufruf ausschließlich mit dem Token authentifiziert. Geprüft werden Aussteller und Signatur über die Realm-Metadaten, Lebensdauer (eine Minute Toleranz), Audience und das azp gegen AllowedClientIds; die Identität trägt das Schema als Typ, damit eine Browsersitzung mit zufällig gleichem azp nie zählt. Der Dienst prüft dieselbe Policy noch einmal. Konfiguration Hearings:PublicationFeed (Enabled, Authority, Audience, AllowedClientIds, RequireHttpsMetadata): ausgeschaltet hat das Schema keine Signaturschlüssel und weist jedes Token ab; eingeschaltet ohne Aussteller, Audience oder Client bricht der Start ab. Der Keycloak-Client (Service-Account, Client Credentials, Audience-Mapper) wird im Kundenrealm angelegt, sobald der Websitebetreiber feststeht; der lokale Stack provisioniert ihn nicht. Abruf und anonyme Übersicht laufen hinter der Aufrufbremse hearing-portal (je Adresse); der Abruf schreibt keinen Audit-Eintrag je Poll, sondern eine strukturierte Logzeile.

Seit RAL-QM-HP-78 (ADR-20): - Seiten: Der Abruf liefert Seiten von höchstens 500 Einträgen mit Fortsetzung (Keyset über die öffentliche Kennung). IsComplete gilt nur für die letzte Seite eines vollständigen Abrufs. - Quittung: Die Website quittiert unter derselben Policy mit POST /api/gz/hearings/public/receipts. Das Portal speichert je Kennung nur den bestätigten Änderungszeitpunkt und den Client, nichts über Personen. Je Quittung entsteht kein Audit-Eintrag. Die Website quittiert mindestens alle 15 Minuten und gibt die Änderungszeit exakt zurück. - Website-Client: reconcile-website-client.sh legt ihn an, wenn sein Secret gesetzt ist. Es wird von reconcile-client.sh eingebunden und läuft damit auch im Deploy-Einstieg. Konfiguration im Portal: Hearings:PublicationFeed mit Audience=ral-qualitymarks-publication-feed und dem Client in AllowedClientIds. - Störung: Der Worker HearingPublicationSyncWorker meldet einen überfälligen oder stummen Abgleich nach Hearings:PublicationSync:StaleAfter (60 Minuten) als Störung PublicationSync und löst sie mit dem nächsten gesunden Lauf auf.

Kontaktmeldungen lesen und entscheiden (RAL-QM-HP-41)

RAL-QM-HP-41 führt kein Recht ein und ändert keine Rollenkomposition. Melden und Zurückziehen verlangen RALQM.Hearings.Portal.Access und die Prüfkette des Portals; Institution, Akteur und Empfänger stammen nie aus dem Aufruf. Intern liest Meldungen, wer Beteiligte liest (RALQM.Hearings.Participants), also auch die Geschäftsführung. Jede Entscheidung verlangt Participants.Manage. Eine Übernahme, die einen Stammdatenkontakt anlegt, verlangt zusätzlich RALQM.Institutions und .Update: Hearings prüft beide vor dem ersten Speicherschritt, der Institutions-Vertrag prüft sie beim Aufruf erneut. „Nur dieses Verfahren“ und Verwerfen brauchen kein Institutionsrecht. Ohne Leserecht auf Institutionen zeigt die Entscheidungsgrundlage keinen Treffer gleicher E-Mail und bietet die Stammdaten nicht an. HearingContactReportAppService trägt an der Klasse RALQM.Hearings und an jeder Methode ihr eigenes Recht (UND-Verknüpfung, wie oben).

Datensparsamkeit: Der Portalvertrag hat kein Feld für die interne Notiz, das Ziel oder die stattdessen übernommene Person; der Hinweis vor Bescheid, Abschluss und Abbruch trägt nur Institutionsnamen. Entität, Personen-DTOs sowie Personen- und Notizfelder der Eingaben sind [DisableAuditing]; der Portaldienst ist es als Ganzes. Der Verlauf nennt Akteur, Zeitpunkt, Beteiligung, Ziel und Kontaktbezüge, nie Angaben der Person. Nachweise: HearingContactReportContractTests (Vertragsform), HearingContactReportAppServiceTests und HearingContactReportPortalTests (Rechte und Abweisungen), HearingContactReportSqlServerTests (echter ABP-Proxy auf SQL Server je Rolle; mit eingeschaltetem Audit steht keine Angabe der Person oder der Notiz in den Audit-Tabellen).

Sitzung und Rücksprung (RAL-QM-HP-52, Stufe A)

Der freigegebene Hoststandard ist 15 Minuten Inaktivität bei maximal acht Stunden Gesamtdauer. Authentication:Session:IdleTimeout und CookieLifetime werden beim Start validiert; UseSlidingExpiration bleibt false. Die prozesslokale CAF-Sitzung bindet Tabs derselben Anmeldung, serverseitige UTC-Zeit und das Provider-Refresh-Token. Ein Hostneustart verlangt neue Credentials. Hintergrundverkehr verlängert das Inaktivitätsfenster nicht. Die Shell meldet echte Bedienereignisse höchstens einmal pro Sekunde mit Antiforgery-Nachweis; die Fünf-Minuten-Prüfung von ABP-Status und Security Stamp bleibt zusätzlich erhalten. HTTP-Middleware und CircuitHandler verweigern abgelaufene oder widerrufene Sitzungen vor Weitergabe an Fachcode.

Nach einem bestätigten Session-401 wird der Browserübergang zur Anmeldung terminal. Bevor Ausblenden oder ein gleichzeitiger Reconnect den Fokus verschiebt, schützt der Host genau den nächsten Blur des zu diesem Zeitpunkt fokussierten Controls vor einem späten Circuit-Callback. Das ist der enge Produktbehelf UP-67, kein Fehlerfilter: gewöhnliche Blur-Ereignisse, temporäre Verbindungsverluste und sonstige JavaScript-Fehler bleiben unverändert sichtbar. Der kontrollierte Browserfall verwendet reale Kontosperre, Session-401 und den eigenen echten Blazor-WebSocket.

Shell und Account-Filter erlauben nur lokale Rücksprungziele einschließlich Query. Kodierte Separatoren und Account-/Callback-Schleifen führen nach /. Der Zielzugriff wird erneut autorisiert. Abmeldung widerruft die lokale Sitzung vor dem OIDC-Aufruf; ein serverseitiger Providerfehler zeigt die neutrale Abmeldeseite mit HTTP 503, deren einzige Handlung die erneute Anmeldung ist (RAL-QM-HP-91 E1): Wer sich beim Anbieter abmelden will, meldet sich erneut an und danach ab, sobald der Anbieter erreichbar ist. Eine eigene Wiederholung der Providerabmeldung gibt es nicht; ist die Providerkonfiguration im Hostprozess zwischengespeichert, leitet der Host auch bei ausgefallenem Anbieter dorthin weiter und der Browser zeigt seinen Netzwerkfehler. Geschützte Antworten verwenden no-store; die Shell lädt Seiten aus dem Back/Forward Cache neu. Ein neuer OIDC-Login verlangt ausdrücklich Credentials statt stiller SSO.

Jede Abmeldung des Anwendungs-Cookies löscht über ABP Account Pro zusätzlich die Zeile der ABP-Sitzung (AbpSessions). Die Abmeldeseite stellt das DELETE nur in ihre Unit of Work, die erst nach dem Handler geschrieben wird. Melden zwei Anfragen derselben Sitzung gleichzeitig ab, etwa die Abmeldeseite und eine Anfrage eines zweiten Tabs, die CAFSessionMiddleware nach dem CAF-Widerruf abweist, oder zwei Abmeldungen, dann entfernt eine von beiden die Zeile zuerst. Eine bereits entfernte Zeile gilt als erledigter Entzug (RAL-QM-HP-92). Das sichert CAF Authentication zu (CAFRevokedSessionWriteInterceptor, für jeden DbContext über AbpDbContextOptions.PreConfigure): Unterdrückt wird genau die EF-Concurrency-Meldung, deren Einträge ausschließlich gelöschte oder geänderte IdentitySessions sind; geändert heißt hier das LastAccessed-UPDATE, das ABPs Sitzungsprüfung im selben Request vormerkt. IdentitySession hat keinen Concurrency-Token, „keine Zeile betroffen“ heißt dort also „schon entfernt“, und das Unterdrücken schreibt nichts. Jeder andere Konflikt bleibt ein Fehler.

Keycloak besitzt die neutralen DE/EN-Anmeldefehler und LOGIN_ERROR-Ereignisse. Die freigegebene Aufbewahrung beträgt 14 Tage; der Reconciler verwendet KEYCLOAK_LOGIN_EVENTS_EXPIRATION_SECONDS (Standard 1209600, positive ganze Sekunden). Zugriff auf Ereignisgründe bleibt beim Keycloak-Betrieb, ohne neue App-Auditfläche. Seit RAL-QM-HP-18 widerrufen Kontosperre, Kennungswechsel und Papierkorb die CAF-Sitzungen des betroffenen Kontos sofort (CAFSessionStore.RevokeSubject, Host-Adapter CAFSessionAccountRevoker), zusätzlich zur Keycloak-Sperre und dem Logout aller Provider-Sitzungen; die nächste Aktion des Kontos landet auf der Anmeldung. Der Schlüssel des Widerrufs ist der NameIdentifier des Anwendungs-Cookies, also die lokale Benutzer-Id (ABP Claims-Factory) — nicht das Keycloak-Subject, das nur bei den Seed-Konten mit der Id übereinstimmt. Details und Abnahmestand: SPEC-RAL-QM-HP-52.

Organisationsanmeldung (RAL-QM-HP-63, ADR-15)

Beide Anmeldewege enden im selben Keycloak-Benutzer. Der Host prüft vor dem externen Anmeldeticket über IPortalLoginAccountReader das validierte sub: genau ein aktives, nicht gelöschtes Hostkonto und genau eine lokale Produktrolle. Authorization bezieht Papierkorb- und Tenantzeilen in die Eindeutigkeitsprüfung ein, ohne sie zuzulassen oder zu verändern. Institutionskonten dürfen nur den Passwortweg verwenden. Die bestehende technische Bootstrap-Ausnahme (admin ohne Produktrolle) bleibt auf diesen Weg begrenzt; sie vergibt keine Fachgrants. Ein abgewiesener Callback endet vor Kontoverknüpfung, Passwortabschluss und Erfolgsaudit auf derselben anonymen, lokalisierten Fehlerseite /fehler/403, ohne Kontoanlage oder E-Mail-Abgleich. Die CAF-Sitzungsprüfung bleibt bestehen. Die Zuordnung Organisationskonto → Konto legt nur der Systemadministrator an (IStaffAccountAppService.SetOrganisationAccountAsync, Recht der internen Kontenwelt, Verlaufseintrag 160); Institutionskonten erhalten keine. Schreibvorgänge nehmen erst die Anwendungssperre RALQM.OrganisationLink, dann die Kontoreservierung, prüfen den Halter vor dem Entfernen des bisherigen Links, stellen ihn bei Fehlern wieder her und erhöhen die Revision des Kontos (nicht den Security Stamp). Ein Kennungswechsel reserviert das Konto ebenfalls und entfernt die Zuordnung vor dem Logout. Den Ausfallweg steuern OrganisationLoginOptions (Prüfadresse, 60 s, 3/3) und die globale Einstellung RALQM.OrganisationLogin.ForceFallback hinter RALQM.OrganisationLogin.Manage; jede Änderung und jede validierte Anmeldung (Weg, Benutzer, Subject) steht im ABP-Sicherheitsprotokoll. Providerablehnungen sieht Keycloak (LOGIN_ERROR, IDENTITY_PROVIDER_LOGIN_ERROR); eine lokal unzulässige Zuordnung erzeugt keinen erfolgreichen Portal-Anmeldeeintrag.

Getrennte Lese- und Pflegerechte am Beispiel der Wertelisten

Der Slice ValueLists definiert drei Rechte, wo zwei naheliegend gewesen wären:

Recht Bedeutung Vergeben an
RALQM.ValueLists die Pflegeseite /administration/wertelisten sehen nur RALQMAdministrator
RALQM.ValueLists.Manage anlegen, umbenennen, sortieren, deaktivieren, Standardfristen setzen nur RALQMAdministrator
RALQM.ValueLists.Read Auswahloptionen für Dropdowns auflösen alle drei internen Rollen

Das dritte Recht existiert, weil Referent und Geschäftsführung ihre Auswahlfelder füllen müssen, ohne die Pflegeseite betreten zu dürfen. Es lässt sich nicht einsparen: ABP verbindet in MethodInvocationAuthorizationService die [Authorize]-Angaben der Klasse und der Methode mit logischem UND und leitet zur Prüfzeit keine Eltern-Kind-Implikation aus der Permission-Definition ab — AddChild ist eine Definitions- und Oberflächenangelegenheit, kein Prüfmechanismus. Wer Manage besitzt, besitzt deshalb nicht automatisch Read; wer Read besitzt, kommt nicht an die Pflegeseite. Aus demselben Grund trägt der Pflegedienst auf Klassenebene nur das schwächste Recht (RALQM.ValueLists) und jede schreibende Methode zusätzlich Manage — ein strengeres Klassenattribut würde die lesende Methode unerreichbar machen, statt sie zu schützen. Ein hearings- oder institutionseigenes Leserecht schied aus: Es hätte eine Referenz von ValueLists.Contracts auf jene Contracts verlangt und damit einen Projektzyklus.

Read ist ein Geschwisterrecht, kein Kind — und das ist eine Vergabe-Entscheidung. Zur Prüfzeit spielt die Definitionshierarchie keine Rolle (siehe oben), zur Vergabezeit aber sehr wohl: Die Rollenoberfläche (RolePermissionsPage.ApplyGrant, aus der ABP-eigenen Permission-UI übernommen) setzt beim Anhaken jedes Elternrecht und beim Abhaken jedes Kindrecht innerhalb derselben Gruppe. Als Kind von RALQM.ValueLists hätte das Anhaken des Leserechts für Referent und Geschäftsführung also zwangsläufig auch die Pflegeseiten-Permission vergeben — genau das, was die Route und der Menüeintrag prüfen — und das Abhaken von RALQM.ValueLists hätte das Leserecht mitentzogen. Der Zielzustand wäre über die Oberfläche weder herstellbar noch reparabel gewesen, sondern nur durch den Seeder. Manage bleibt bewusst Kind von RALQM.ValueLists: Wer den Katalog bearbeiten darf, muss die Seite auch öffnen können. Durchgesetzt wird beides von ValueListsPermissionContractTests (Definitionsform) und RolePermissionUiTests::Granting_only_the_value_list_lookup_right_leaves_the_maintenance_permission_alone sowie …::Withdrawing_the_maintenance_permission_keeps_the_value_list_lookup_right — beide bauen die Gruppe aus dem echten Provider, nicht aus einer abgeschriebenen Form.

Institutionspflege durch den Referenten

Seit RAL-QM-HP-10 pflegt der Referent die Institutionsstammdaten selbst; zuvor hielt die Rolle nur Lesen und Export.

Recht Bedeutung Vergeben an
RALQM.Institutions Liste und Bearbeitungsmaske lesen, Dublettensuche (Namensabgleich), Nennung laufender Verfahren je Kontakt alle drei internen Rollen
RALQM.Institutions.Create anlegen, mit Dublettenwarnung; prüft auch der CSV-Import RALQMAdministrator, RALQMCaseOfficer
RALQM.Institutions.Update ändern, umbenennen mit Dublettenwarnung, deaktivieren (bei laufenden Verfahren nur mit Bestätigungskennzeichen im Aufruf), reaktivieren; Kontakte anlegen und ändern, Standardansprechpartner festlegen, Kontakte deaktivieren (den Standard nie), Vertretung zuweisen, Pflichtpartner-Kennzeichen setzen (nur mit aktivem Standard) und entfernen RALQMAdministrator, RALQMCaseOfficer
RALQM.Institutions.Export Institutions- und Kontaktliste exportieren RALQMAdministrator, RALQMCaseOfficer
RALQM.Institutions.Delete löschen, geschützt über den Nutzungsport nur RALQMAdministrator

Institutionen werden deaktiviert, nicht gelöscht; Delete bleibt deshalb beim Administrator. Create schaltet zugleich den CSV-Import frei, weil der Import dasselbe Recht prüft — eine hingenommene Nebenwirkung (Spec A5), kein eigenes Import-Recht. Durchgesetzt von PermissionOwnershipTests::Case_officer_maintains_institutions_without_deleting_them (Komposition aller drei Rollen) und InstitutionAuthorizationPipelineTests::Case_officer_composition_creates_and_updates_through_the_real_abp_proxy_but_never_deletes (realer ABP-Proxy gegen SQL Server).

Die Kontaktregeln aus RAL-QM-HP-9 laufen unter demselben Recht Update und sitzen im Aggregat Institution: höchstens ein Standardansprechpartner je Institution, immer aktiv, der erste aktive Kontakt wird es selbsttätig, ein Wechsel wird vor den übrigen Kontaktänderungen desselben Aufrufs angewendet und als zwei Speicherschritte in einer Transaktion gespeichert; der Standard kann weder deaktiviert noch entfernt werden; eine Vertretung ist ein anderer aktiver Kontakt derselben Institution mit Zeitraum; das Pflichtpartner-Kennzeichen wird zuletzt gesetzt, damit sein Guard den Kontaktstand desselben Aufrufs sieht, und der Guard greift nur beim Setzen: Ein bestehendes Kennzeichen bleibt auch ohne aktiven Standard bestehen (RAL-QM-HP-9 E13). Die Deaktivierung eines Empfängers laufender Verfahren (RAL-QM-HP-9 AC-5, zweiter Guard) verlangt ein Bestätigungskennzeichen je Kontaktzeile (UpdateInstitutionContactDto.ConfirmDeactivation). Der Anwendungsdienst fragt bei jedem Speichern erneut, und zwar zuletzt: nach allen fachlichen Prüfungen des Aufrufs und vor dem ersten Speicherschritt (RAL-QM-HP-9 E14). Er fragt über IInstitutionContactUsageProvider die laufenden Verfahren aller Kontakte ab, die der Aufruf ohne Kennzeichen deaktiviert, und weist mit RALQM.Institutions:ContactDeactivationRequiresConfirmation ab. Die Abweisung nennt alle unbestätigten Empfänger mit ihren Verfahren und die Zahl der verschiedenen Verfahren als Exception-Daten; nichts wird gespeichert. Weil jede fachliche Abweisung vorher kommt, etwa der Standard-Guard oder die Vertretungsregeln, bestätigt der Aufrufer nur ein Speichern, das sonst gelingt. Die Bestätigung der Institution (RAL-QM-HP-10 AC-10) bleibt vor der ersten Mutation: Deaktiviert ein Speichern Institution und Empfänger-Kontakt zugleich, kommen beide Fragen nacheinander, die der Institution zuerst. Die Oberfläche fragt noch nicht nach; der Bestätigungsdialog folgt mit der Institutionsmaske (RAL-QM-HP-9, Schritt Institutions-Oberfläche).

Lese- und Pflegerecht der Institutionsgruppen (RAL-QM-HP-3)

Der Slice Institutions definiert für die Institutionsgruppen (SPEC-RAL-QM-HP-3) zwei eigene Rechte:

Recht Bedeutung Vergeben an
RALQM.InstitutionGroups Gruppenübersicht, Gruppenmaske und Mitgliederliste lesen alle drei internen Rollen
RALQM.InstitutionGroups.Manage Gruppen anlegen, umbenennen, beschreiben und auflösen; Institutionen zuordnen und entfernen RALQMAdministrator, RALQMCaseOfficer

Muster sind die Institutionsrechte, nicht die Wertelisten: Ein drittes Recht braucht es nicht, weil keine Rolle Gruppen lesen muss, ohne die Übersicht zu sehen. Referent und Systemadministrator halten beide Rechte ausdrücklich, weil ABP zur Prüfzeit aus Manage kein Leserecht ableitet (siehe Wertelisten). Die Rolle Institution erhält keines. In der Definition ist RALQM.InstitutionGroups ein eigener Wurzelknoten der Gruppe RALQM, kein Kind von RALQM.Institutions; als Kind würde das Anhaken in der Rollenoberfläche auch das Institutionsrecht vergeben. Manage ist Kind des Leserechts: Wer Gruppen pflegt, muss sie öffnen können. Durchgesetzt von PermissionOwnershipTests::Institution_groups_are_read_by_every_internal_role_and_maintained_by_case_officer_and_administrator (Komposition aller vier Rollen), PermissionOwnershipTests::Permission_definitions_have_the_expected_parent_child_relationships (Definitionsform) und InstitutionGroupAuthorizationPipelineTests::The_manage_right_maintains_groups_while_the_read_right_alone_only_reads (realer ABP-Proxy gegen SQL Server: Lesen erlaubt, jede Pflegeoperation abgewiesen, Daten unverändert).

Der Gruppendienst trägt das Leserecht auf der Klasse und Manage an jeder Schreiboperation — dieselbe Form wie bei den Wertelisten, weil ABP Klassen- und Methodenattribut mit logischem UND verbindet: Ein strengeres Klassenattribut würde das Lesen unerreichbar machen. Fachfehler der Gruppen tragen den eigenen Namensraum RALQM.InstitutionGroups, der in den Institutions-Contracts auf dieselbe Ressource abgebildet wird.

Kontoprovisionierung

Zwei getrennte, kontrollierte Wege binden Menschen an Keycloak-Konten — beide über denselben schmalen, fail-closed Provisionierungsport, nie über E-Mail-Gleichheit:

  • Institutionskontakte: RAL legt die dedizierten Institutionskonten an — ausgelöst aus der institutionsübergreifenden Kontaktsuche, keine Selbstregistrierung; je Institution mehrere Benutzerkonten (je Kontakt eines), korreliert über sub. Ein vorhandenes Konto erhält eine erneute Einladung statt eines Dublettenkontos, Mehrdeutigkeit scheitert geschlossen (ExternalAccountProvisioningErrorCodes).
  • Mitarbeitendenkonten: ausschließlich über /administration/mitarbeitendenkonten, mit Name, E-Mail und genau einer internen Produktrolle; Rollenwechsel, Aktivieren/Sperren, Kennungswechsel, Wiedereinladung, Papierkorb und Wiederherstellen sind optimistisch concurrency-geschützte, auditierte Writes mit Verlaufseintrag (StaffAccountErrorCodes); Sperre und Kennungswechsel wirken sofort auf Keycloak und die offenen Sitzungen. Institutionskonten tragen die Rolle Institution und eine Bindung an Institution und Ansprechpartner; Referent und Systemadministrator sehen und pflegen sie in derselben Übersicht, interne Konten nur der Systemadministrator. Die ABP-Benutzerseite ist kein unterstützter GZ-Kontenpfad. Die Einladung (Keycloak Execute-Actions) gilt 7 Tage (actionTokenGeneratedByAdminLifespan); ein älterer Einladungsverweis lässt sich nicht widerrufen, nur durch Ablauf entwerten.

Selbstbedienung am eigenen Konto (RAL-QM-HP-13)

Der Kontoinhaber pflegt sein Konto über IOwnAccountAppService — einen Dienst, der ausschließlich am Konto des Aufrufers arbeitet und keine Konto-Id entgegennimmt; er ist lediglich durch Angemeldetsein autorisiert, weil auch eine Rolle ohne jedes Kontenrecht ihre eigenen Angaben pflegt. Die Oberfläche dazu (OwnAccountPanel) liegt im geteilten UI-Baustein src/UI/Blazor/Account, weil die interne Shell (/konto) und das Institutionsportal (/portal/konto) dieselbe Komponente einbetten und ein Feature-UI kein fremdes Feature-UI referenzieren darf.

Passwörter verlassen Keycloak nie. Weder Erstvergabe noch Änderung noch Zurücksetzen laufen über das Portal: die Anwendung startet die Keycloak-Aktion UPDATE_PASSWORD über einen Host-Endpunkt mit max_age=0 und prompt=login, damit auch eine gerade genutzte Provider-Sitzung das aktuelle Passwort erneut verlangt. Keycloak authentifiziert dabei erneut und meldet das Ergebnis im Rücksprung. Erst dann erneuert die Anwendung den Security Stamp, beendet alle übrigen Sitzungen des Kontos — lokal über CAFSessionStore.RevokeSubject mit Ausnahme der aktuellen Sitzung, beim Provider über die Sitzungsliste — und bestätigt per E-Mail. Ein abgebrochener oder vom Provider als fehlgeschlagen gemeldeter Rücksprung ändert nichts. Ein Architekturtest hält fest, dass Vertrag, Port und Oberfläche kein Passwortfeld, keine Passworteingabe und keinen Reset-Endpunkt tragen.

Der Passwortabschluss reserviert das kontogebundene Vorgangstoken exklusiv. Zuerst beendet er die übrigen Provider-Sitzungen; Security Stamp und Kontoverlauf werden danach in einer eigenen transaktionalen ABP-Unit-of-Work festgeschrieben. Erst der erfolgreiche Commit verbraucht das Token und erlaubt CAF-Sitzungswiderruf und Bestätigungsmail. Provider- oder Persistenzfehler geben die Reservierung für einen Retry innerhalb der ursprünglichen Frist frei. Die Mail bleibt nach Commit best effort, auch bei Abbruch. Der Host erneuert bei Erfolg das Cookie und leitet permissionbasiert nach /konto oder /portal/konto, jeweils mit Ergebnisparameter und erhaltenem PathBase.

Den Aktionsstatus übernimmt der Host aus TokenValidatedContext.ProtocolMessage, unabhängig von GET oder form_post. Der vorhandene CAF-Validator läuft zuerst; ein abgewiesenes Ergebnis wird nicht übernommen. Bis zum bestehenden Ticket-Admission-Guard bleibt die bekannte Statuskategorie ausschließlich im laufenden HttpContext.Items, an denselben Vorgang gebunden. Erst danach wird sie am Einmalvorgang gespeichert. Fehlende/unbekannte Werte und mehrfache Statusparameter bleiben geschlossen. Weil die native OIDC-Nachricht bei Mehrfachwerten nur den ersten behält, prüft der Host zusätzlich die Anzahl in der bereits geparsten Query/Form (IFormFeature.Form), ohne die Form erneut zu lesen. Der server-only Start am bestehenden IOwnAccountAppService bindet den Vorgang an RequireMineAsync und dessen record.Subject; das Subject wird nicht als DTO oder aus einem optionalen Anwendungs-Cookieclaim transportiert. Das validierte Provider-Subject muss beim Attach unter derselben Store-Sperre exakt zum gespeicherten Subject passen. Der spätere Passwort- und Zweitfaktorabschluss prüft zusätzlich die aktuelle Kontozuordnung, bevor Provideraufrufe, Security Stamp, Verlauf, Sitzungswiderruf oder Mail folgen. Eine zwischenzeitliche Umverknüpfung sowie fehlende/fremde Subjects bleiben ohne diese Wirkungen. Der reale Gegenbeleg hp13callback20261016a auf 41491906 war POST/Form=success, Query=MISSING und Portal=failed; die transportunabhängige Korrektur benötigt ihren neuen Lauf.

Das geteilte Kontoformular bietet nach einem Lesefehler einen erneuten Ladeversuch. Bei veraltetem Stempel bleiben Eingaben erhalten, bis der Inhaber ausdrücklich „Aktuellen Stand laden und Eingaben verwerfen“ wählt. Erst diese Aktion ersetzt Formular und Stempel gemeinsam. Speicherbestätigung und Validierungsfehler erhalten nach dem interaktiven Render den Fokus; jedes Profilfeld hat Label und eigene Validierungsmeldung.

Zweiter Faktor (RAL-QM-HP-11, ADR-19)

Der zweite Faktor ist ein Keycloak-TOTP. Das Konto-Panel zeigt nur, ob einer eingerichtet ist, und startet über die Host-Endpunkte /konto/zweiter-faktor-einrichten und /konto/zweiter-faktor-entfernen die Keycloak-Aktionen CONFIGURE_TOTP bzw. delete_credential:{id} (mit max_age=0). Geheimnis, QR-Code und Codes bleiben bei Keycloak; der Admin-Port liest vom Credential nur Id und Typ. Der Abschluss entscheidet am Credential-Bestand, beendet dann die übrigen Sitzungen, erneuert den Stamp und schreibt den Kontoverlauf. Ein Systemadministrator setzt den Faktor eines anderen Kontos über einen persistenten, fortsetzbaren Auftrag zurück; der Kontoinhaber erhält eine Benachrichtigung ohne Zugangsdaten. Die Kontenübersicht zeigt den Faktor (eingerichtet, nicht eingerichtet, unbekannt) und offene Rücksetzaufträge.

Nach bestätigter Entfernung des gebundenen Faktors werden dessen Credential-IDs im selben persistierten Auftragsschritt geleert. Bis zum erfolgreichen Providerabschluss bleiben sie für Wiederanlauf und Schutz eines später neu eingerichteten Faktors gebunden. Operation-ID, Schritt, Akteur und Zeitpunkte bleiben für Fortsetzung und Replay-Schutz erhalten; bereits gespeicherte Altaufträge werden dadurch nicht rückwirkend bereinigt. Die noch zu bestätigenden Aufbewahrungsregeln für Aufträge, Kontoverlauf und Betriebsprotokolle stehen in der HP11-Spec.

Die Kommunikationssprache folgt dem einzigen HP101-Modell: interne Konten tragen RALQM.Profile.CommunicationLanguage in ihren ExtraProperties, Institutionskonten lesen über den Institutions-Contract ausschließlich ihren gebundenen Kontakt. Die getrennte Eigenkonto-Sprachaktion überträgt die Locale vor dem Speichern zum Provider; der Kontoverlauf hält nach ADR-22/E24 alte und neue Sprache fest. Es gibt dafür kein zusätzliches ABP-U-Setting. Die Rücksetzmail liest bei Auftragsanlage die aktive Empfängerpräferenz; fehlend oder inaktiv bedeutet hier ausdrücklich de-DE, unabhängig von HP101s veränderlichem globalen Standard. NoticeCultureName in den ExtraProperties des Auftrags hält diese Kultur fest. Retry und Altauftrag fragen keine aktuelle Präferenz ab; ein fehlender Alt-Snapshot bedeutet Deutsch. Der Snapshot bestimmt die Lokalisierung von Betreff/Text und die Darstellung des ursprünglichen UTC-Rücksetzzeitpunkts über HP101s HearingMessageInstants. Empfängeradresse und Anzeigename werden weiter beim Versand gelesen, die vollständige Mail wird nicht eingefroren. Dies ist die begrenzte HP11-Sonderregel aus ADR-19; die übrigen Kommunikationswege aus ADR-22 bleiben erhalten.

Angeforderte Authorize-Parameter reicht CAF Authentication nur präfixgebunden durch (CAFAuthorizeParameters); übrige Authentication-Properties sind interner Zustand und erreichen den Anmeldedienst nie.

Die persönlichen Angaben (Anrede, Titel, Funktionsbezeichnung, Vor- und Nachname, Telefon, Mobil) liegen führend am ABP-Benutzer — Name und Telefon in dessen eigenen Spalten, der Rest als ExtraProperties; eine zweite Tabelle entsteht nicht. Nach Keycloak gespiegelt wird ausschließlich der Name, damit dessen eigene Mails korrekt adressieren. Anmeldename und Anmeldekennung sind nicht selbstbedienbar (RAL-QM-HP-18 AC-6), die Stammdaten der Institution bleiben unberührt.

Die Fehlversuchssperre ist von der Deaktivierung getrennt: sie entsteht in Keycloaks Brute-Force-Erkennung, ändert den Aktivstatus nicht und wird in der Kontenübersicht zusätzlich zum Aktivstatus angezeigt. Aufheben darf sie allein der Systemadministrator über das eigene Recht RALQM.Accounts.Unlock; der Sperrzustand wird je Seite beim Provider gelesen und fällt bei dessen Ausfall auf „unbekannt“ zurück, ohne die Übersicht zu blockieren. Einen Filter auf den Sperrzustand gibt es bewusst nicht: Keycloak kennt keine Liste gesperrter Konten, der Zustand träfe erst nach der Seitenabfrage ein und ein Filter würde Gesamtzahl und Seitenfolge verfälschen.

Details zu Dokument- und Portalzugriffswegen (intern, Institutionskonto, Direkt-Share; im Host gesperrter CAF-Direktshare) stehen in Laufzeitsicht; Multi-Tenancy ist deaktiviert, Keycloak-Gruppen ersetzen keine GZ-Rollen oder ABP-Permissions.

Aktivierungszustimmung als Zugangsbedingung (RAL-QM-HP-39)

Ein Institutionskonto erhält Fachzugang erst mit einem Aktivierungsnachweis. Die Bedingung sitzt in derselben einzigen Prüfung InstitutionAccountAccessReader.GetCurrentAsync, durch die jeder Portalaufruf läuft; fehlt der Nachweis, antwortet jeder Portaldienst mit PortalAccessMissing. GetActivationCandidateAsync liefert dasselbe Konto ohne diese eine Bedingung und dient nur dem Zustimmungsschritt (IConsentActivationAppService), der dadurch selbst keine Kontoregeln nachbaut. Die Weiterleitung von Portalseiten auf /portal/zustimmung ist Bedienhilfe und prüft nur den Nachweis in der Datenbank; die Sicherheitsgrenze bleibt die Zugangsprüfung. Sie liest in einer eigenen, nicht transaktionalen Unit of Work: ABPs Anfrage-Unit-of-Work (UseUnitOfWork) überspringt Razor-Komponenten-Endpunkte, und der Nachweisleser fragt in der Unit of Work seines Aufrufers. Ohne sie lief die Abfrage auf einem bereits verworfenen DbContext, jede Prüfung scheiterte als technischer Fehler, und kein neues Konto erreichte die Zustimmungsseite (Korrektur 02.10.2026). Die Regel dahinter: Eine Abfrage aus GetQueryableAsync muss innerhalb der Unit of Work ausgeführt werden, die sie erzeugt hat. Ohne umgebende Unit of Work öffnet und schließt ABP für den Aufruf GetQueryableAsync allein eine, und die spätere Ausführung über AsyncExecuter trifft deren verworfenen DbContext. Der Nachweisleser (ConsentProofStore) baut solche Abfragen; wer ihn außerhalb einer Unit of Work aufruft, öffnet sie deshalb selbst. Pflege der Fassungen ist ein Verwaltungsrecht (RALQM.Consents.Documents, .Publish) allein des Systemadministrators; Nachweise sind nur in der Rückmeldungsansicht unter Hearings.Responses.Read sichtbar und haben keinen eigenen Endpunkt (ADR-11).

2. Persistenz und EF Core

RAL-QM-HP-72 serialisiert Kontoverwaltung und neue Zuständigkeiten über die Authorization-eigene Kontenzeile. Reguläre Deaktivierung, interner Rollenwechsel und Papierkorb reservieren sie mit aktuellem Stempel vor der Hearings-Nutzungsprüfung und vor jedem Provideraufruf. Anlage und Übergabe eines Verfahrens reservieren das Zielkonto über den prozesslokalen IStaffResponsibilityReservation in derselben Transaktion. Die anschließende frische Eignungsprüfung liest nur dieses Konto; sie hält keine anderen Kontosperren. Bei Anlage bleibt der berechtigte aktive interne Ersteller zuständig; das bestehende Anlegerecht wird nicht auf die Referentenrolle verengt. Übergaben verlangen weiterhin einen aktiven Referenten als Ziel. Die Klärungsliste erkennt aktive interne Ersteller einschließlich Geschäftsführung und technischem Bootstrap-Administrator als gültige Zuständige; fehlende, inaktive oder nicht eindeutig interne Konten bleiben Klärungsfälle. Hearings beantwortet IHearingResponsibilityGuard für alle nicht abgeschlossenen oder abgebrochenen Verfahren. Explizites EmergencyDeactivateAsync darf offene Zuständigkeiten belassen, erzeugt EmergencyDeactivated und widerruft Provider-/lokale Sitzungen. Die separate GF-Klärungsliste /anhoerungen/zustaendigkeit-klaeren verlangt Hearings.Default und Hearings.Approve; persönliche Freigabeaufgaben bleiben auf PendingApproval beschränkt.

Verfahrensupload und Versionsersatz (RAL-QM-HP-75) bewahren die ABP-Auditaktion mit Akteur, Operation, IDs, Typ/Größe, Stempel und Fachmerkmalen. Dateiname, Stream, Anzeigename und Beschreibung bleiben aus den Parametern ausgeschlossen. Die ausgeschlossenen Pflichtwerte tragen [Required] für die ABP-Proxy-Validierung, ohne C# required: diese Kombination mit JsonIgnore würde die Vorbereitung des Audit-Serialisierers abbrechen. Der echte ABP-/SQL-Pfad prüft die gespeicherten erlaubten Metadaten. HearingDocument bleibt außerhalb der Entity-History-Allowlist.

RAL-QM-HP-70, RAL-QM-HP-71 ergänzen zusätzliche Hearings-Empfängerstände und Erinnerungsberichte mit unveränderlicher Auswahl und festem internem Empfänger. ReminderRunId korreliert Hauptaufträge, weitere Empfänger, Vertretungskopien und korrigierte Folgeaufträge. Der Bericht liest aktuelle Transportzustände; er ist unter Hearings.Default geschützt. Seine kurze E-Mail trägt nur den geschützten Link. Concurrency schützt Benachrichtigungsclaims. Ein Prozessabbruch nach SMTP-Annahme vor Speicherung kann eine erneute Benachrichtigung auslösen (At-least-once).

  • SQL Server ist der einzige relationale Provider; es gibt genau einen HearingPortalDbContext und einen Migration Host im Projekt HearingPortal (Host).
  • Jedes Modul definiert seine Entities und eine eigene Configure{Feature}()-ModelBuilder- Erweiterung in seiner Runtime; der Host ruft diese Erweiterungen nur auf und besitzt keine fachliche Konfiguration.
  • Migrationen werden ausschließlich im Host erzeugt und ausgeführt (HearingPortal/Migrations), gegen eine echte, disposable SQL-Server-Datenbank getestet und mit dotnet ef migrations has-pending-model-changes als Pending-Model-frei nachgewiesen.
  • Beziehungen innerhalb eines Moduls verwenden echte Foreign Keys und Indizes. Über Modulgrenzen werden ausschließlich IDs gespeichert (InstitutionId, DocumentId, seit RAL-QM-HP-4 auch MarkTypeId/ProcedureTypeId auf RALQMHearings, CountryId auf RALQMInstitutions und FunctionId auf RALQMInstitutionContacts — die übrigen neuen Spalten dieser Tabelle aus RAL-QM-HP-9, Phone, IsDefault, DeputyContactId, DeputyFromUtc und DeputyUntilUtc, sind moduleigen, und DeputyContactId ist deshalb ein echter Fremdschlüssel, siehe unten —, seit RAL-QM-HP-16 HearingId und HearingDocumentId auf RALQMFilingHearingUsages) — keine Cross-Module-Navigation, kein Cross-Module-Foreign-Key. Die Wertelisten sind dafür kein Sonderfall, sondern der jüngste Anwendungsfall derselben Regel: Ein Fremdschlüssel auf RALQMProcedureTypes wäre bequem und genau die Kopplung, die die Modulgrenze verhindert. Ob eine so gespeicherte ID gültig ist, prüft der schreibende Anwendungsdienst über den Vertrag des besitzenden Slices, bevor er sie übernimmt: Hearings bestätigt Existenz und Aktivität einer Institution über IInstitutionAppService beim Anlegen eines Verfahrens (seit RAL-QM-HP-10, AC-9), beim Wechsel des Antragstellers, beim Hinzufügen von Teilnehmern und Nachbeteiligten; der Pflichtpartner-Leser liest nur aktive Institutionen. Die DocumentId bezieht Hearings im selben Arbeitsschritt vom Besitzer aus dem Upload, statt sie zu prüfen. Die Anlage weist ab, bevor eine Referenznummer reserviert ist.
  • Mutable Aggregate tragen optimistische Concurrency (ConcurrencyStamp); stale Schreibzugriffe scheitern deterministisch statt still zu überschreiben. Zeitpunkte sind UTC über ABPs IClock.
  • RAL-QM-HP-40: RALQMHearingResponses.SequenceNumber ist durch einen positiven SQL-Check und einen gefilterten Unique-Index über Teilnahme/Sequenz geschützt. Die historische Bestandsmigration setzt vorhandene aktive und soft-gelöschte Zeilen auf 1, ohne Inhalte oder frühere Zeitpunkte zu ändern. Der technische Schreibwechsel begrenzt den Einzelantwortindex auf Sequenz 1, ergänzt die eindeutige Auftragskennung und schützt festgeschriebene Abgaben einschließlich ihrer Anlagenzuordnungen auf dem Persistenzpfad vor Änderung und Löschung. Bestehende Antworten erhalten keine erfundene Auftragsidentität oder Einwilligung.
  • RAL-QM-HP-36: RALQMHearingResponses.Origin hält fest, auf welchem Weg eine Abgabe entstand – 10 Portal, 20 intern erfasst, 0 nicht nachgewiesen (SQL-Check [Origin] IN (0, 10, 20)). Die Migration AddHearingResponseOrigin fügt die Spalte mit dem Standardwert 0 hinzu; alle vorhandenen Zeilen, aktive wie weich gelöschte, lesen danach „nicht nachgewiesen“ – ohne UPDATE und ohne Rückschluss aus der Benutzer-ID. Der Standardwert bleibt an der Spalte, anders als bei SequenceNumber: Dort hätte er eine Tatsache erfunden, hier ist 0 genau die Aussage über eine Zeile ohne festgehaltene Herkunft. Neu schreiben kann die Anwendung ihn nicht: Das Aggregat verlangt beim Anlegen Portal oder InternalRecording, und jeder Schreibweg nennt seinen Wert selbst. Der Rückweg entfernt Check und Spalte; ein erneuter Hinweg liest wieder jede Zeile als „nicht nachgewiesen“ (HearingResponseOriginMigrationSqlServerTests).
  • Fachliche/auditrelevante Daten werden nicht hart gelöscht: Institutionen erst nach Nutzungsprüfung, der Hearing-Prozess über benannte Zustände (Abbruch, Widerruf, Zurückziehen).
  • Kontakte einer Institution werden weich gelöscht und nie aus der Sammlung des Aggregats genommen (RAL-QM-HP-9, A5); Begründung und Nachweise unten. Die Anlagen einer Stellungnahme wurden in RAL-QM-HP-38 ebenso weich gelöscht; seit der Festschreibung durch RAL-QM-HP-40 ist auch diese Entfernung gesperrt (unten).
  • Der Verfahrensverlauf (RALQMHearingHistoryEntries, RAL-QM-HP-7) ist append-only auf dem Persistenzpfad: ein SaveChangesInterceptor der Hearings-Runtime weist jedes Update und Delete eines Eintrags mit HistoryEntryImmutable ab (registriert über AbpDbContextOptions.PreConfigure, weil Configure die eine Standardaktion setzt, die das UseSqlServer() des Hosts ersetzt; nachgewiesen in den erzeugten DbContextOptions und gegen die echte Datenbank), der Lesevertrag kennt keine Mutation, ein Architekturtest hält src frei von Update/Delete-Aufrufen auf dem History-Repository, von ExecuteDelete/ExecuteUpdate und vom Tabellennamen außerhalb des Mappings. Die Zeile trägt nur Ids, Katalogwerte und Zeitpunkte; wer gehandelt hat, löst das Lesemodell beim Lesen über den Lesevertrag der Authorization auf (interne Konten wie das gebundene Institutionskonto einer Portalaktion, das zusätzlich als solches gekennzeichnet wird; seit RAL-QM-HP-87 nie aus den Kontakt-Snapshots des Aggregats, die der Empfängerwechsel aus RAL-QM-HP-6 umschreibt; „System“ ohne Person; ohne benennbares Konto die Kurz-Id). Alte und neue Werte einer Datenänderung kommen aus der ABP Entity History des Verfahrens — gelesen über den Port IHearingEntityHistoryReader auf IAuditLogRepository hinter der Hearings-Berechtigung des Aufrufers, nie über den berechtigungsgeschützten AuditLogging-Application-Service — und werden in der Zeitleiste dem Ereignis derselben Operation zugeordnet (Feld und Zeitfenster; eine Stammdatenkorrektur CoreDataCorrected nennt ihr Feld im Detail). Die Verlaufstabelle bleibt PII-frei, die Entity History ist der Feld-Diff. Das erste Öffnen der Teilnahmeansicht ist Zustand des Teilnehmers (FirstOpenedAtUtc), kein Nachschlagen in der Verlaufstabelle: der Übergang schreibt das Ereignis, der Concurrency-Stamp des Aggregats entscheidet ein Rennen. HearingParticipationOpeningTransaction toleriert den verlorenen Stempelkonflikt und wiederholt ausschließlich ein SQL-Deadlock-Opfer (1205, auch in EF-Ausnahmehüllen) bis zu zweimal nach kurzer Pause. Jeder Versuch erhält eine eigene Unit of Work mit frischer Konten-/Objektprüfung; der verworfene Versuch hinterlässt weder Öffnungsmarke noch Verlauf. Andere Fehler und eine neue Zugriffsabweisung bleiben Fehler. Nachweis: HearingPortalFirstOpeningConcurrencyTests (echter Deadlock, Rollback, frischer Kontext, Abweisung beim Wiederholen und Versuchslimit) sowie HearingParticipationOpeningTransactionTests.
  • Die verfahrensübergreifende Recherche im Verlauf (RAL-QM-HP-45) ist dieselbe Lesesicht über alle Verfahren: Zeitraum, Urheber (internes Konto oder „System“), Verfahren, Institution und Ereignisarten werden in SQL ausgewertet — die Institution über die Kindobjekte der Teilnehmer dieser Institution (Teilnehmer, Grants, Einladungen, Bindungen, Stellungnahmen, Anhänge, Antworten) und über die Institutions-Id als Subjekt, letzteres nur für die drei Ereignisarten, denen das Lesemodell die Institution zuschreibt (Antragsteller-Korrektur, Nachforderung eingereicht/freigegeben) —, ebenso Zählung, Whitelist-Sortierung und Seitenwahl; Namen, Konten, Institutionen und die Feld-Diffs der Entity History löst das mit der Zeitleiste geteilte Lesemodell HearingHistoryReadModel für die Seite auf. Gültige JSON-Strings der Entity History werden genau einmal dekodiert; ungültige oder abgeschnittene Werte bleiben unverändert sichtbar. Der CSV-Writer maskiert die dekodierten Fachwerte. Die Entity History wird je Aufruf in einer Abfrage für alle Verfahren der Seite gelesen (EntityId IN (...)); als Zuordnungskandidaten eines Diffs gelten alle diff-tragenden Verlaufszeilen des Verfahrens, nicht nur die der Seite — ein Diff, dessen Ereignis außerhalb der Seite liegt, erscheint nirgends, nie an einem gleichartigen Ereignis der Seite. Die Zellregeln der drei CSV-Exporte (Semikolon, CRLF, BOM, Formelschutz einschließlich führender Leerzeichen und Steuerzeichen) teilt der modul-lokale Helfer HearingCsvCells. Ein Ende vor dem Beginn wird vor jedem Lesen abgewiesen (HistoryPeriodInverted). Die CSV-Ausleitung der Treffer nimmt dieselben Filter und dieselbe Sortierung ohne Seitengrenze, läuft unter dem Exportrecht des Moduls (RALQM.Hearings.Export), wird oberhalb der Höchstzahl RALQM.Hearings.HistoryExportMaxRows (Standard 5000, gepflegt auf der Seite Fristvorgaben, Teil der Protokoll-Allowlist) vor dem Lesen einer Zeile abgewiesen (HistoryExportLimitExceeded) und hinterlässt am Audit-Eintrag ihres Aufrufs die Zeilenzahl (ExtraProperties["HistoryExportRowCount"] und Kommentar); die CSV-Bytes bleiben Rückgabewert. Eigenständige Datenänderungen ohne Verlaufszeile gehören der Zeitleiste je Verfahren, nicht der Recherche.
  • Protokollierung mit einer benannten Ausnahme: Institution und Kontakt stehen in der ABP Entity History (RALQMInstitutionsEntityHistory), jeder Application-Service-Aufruf im Audit-Log. Die interne Notiz einer Institution (RAL-QM-HP-10, A2) ist per [DisableAuditing] auf der Entity- und der DTO-Eigenschaft von beidem ausgenommen: kein AbpEntityPropertyChanges-Eintrag, kein Schlüssel in den Aktionsparametern. Die Änderung selbst bleibt sichtbar — als AbpEntityChanges-Zeile mit dem Benutzer des Audit-Logs und über LastModifierId/LastModificationTime am Aggregat; ABP schreibt für diese Basisfelder keine Eigenschaftszeilen. Durchgesetzt von InstitutionAuditEntityHistorySentinelTests (Institutions.SqlServer, eigene migrierte Datenbank, Muster des Hearings-Sentinels). Seit RAL-QM-HP-7 (AC-3) gilt dasselbe für die Konfiguration: der Selektor RALQMSettingsEntityHistory erfasst die ABP-Einstellung (Setting), und HearingSettingsEntityHistoryContributor verwirft vor dem Speichern des Audit-Logs jede Einstellungsänderung außerhalb der Allowlist ProtocolledSettingsOptions — der Selektor allein erfasste jede ABP-Einstellung bis hin zu Mail-Zugangsdaten. Die Allowlist ist die Vereinigung der Beiträge der Features: die drei Fristvorgaben (HearingsSettings.ProtocolledNames, Hearings) und seit RAL-QM-HP-27 die sieben Absenderangaben der Organisation (OrganisationSenderSettings.All, Settings-Runtime) sowie seit RAL-QM-HP-45 die Höchstzahl der Protokollausleitung (HearingsSettings.HistoryExportMaxRows). Eine geänderte Fristvorgabe, ein geändertes Absenderfeld oder eine geänderte Höchstzahl steht mit altem und neuem Wert im Audit-Protokoll, DispatchBatchSize und ABP-eigene Einstellungen ohne jede Zeile (SettingsAuditEntityHistorySentinelTests, OrganisationSenderAuditEntityHistorySentinelTests, je Einstellungsname).

  • Absender jeder Aussendung (RAL-QM-HP-27). Die Organisation pflegt Name und Anschrift einmal unter /setting-management/organisation; HearingOutboxManager mischt den Absenderblock als {{Absender}} in jedes Nachrichtenmodell, bevor der Text je Empfänger festgeschrieben wird — so trägt jede danach erzeugte Nachricht den aktuellen Absender, jede bereits erzeugte den ihres Moments. Ohne vollständigen Absender (Name, Straße, PLZ, Ort) verweigert der Manager den Enqueue vor dem ersten Provider-Aufruf (RALQM.Settings:OrganisationSenderMissing, Nullversand wie bei unaufgelösten Platzhaltern); die Anschreibenerzeugung aus RAL-QM-HP-24 erhält dieselbe Sperre über IOrganisationSenderReader.RequireAsync().

Eigenständige Verfahrenskopie (RAL-QM-HP-12)

RALQMHearings trägt nullable ClonedFromHearingId, CloneOperationId, CloneActorUserId und CloneRequestHash (char(64), nicht auditiert). Der gefilterte eindeutige Index IX_RALQMHearings_CloneOperation bindet einen Auftrag an genau ein Ziel, auch wenn das Ziel später weich gelöscht wird. Bestandsverfahren bleiben ohne Herkunft. Herkunft und Auftrag haben keine öffentlichen Setter; der einzige Initialisierungspfad verweigert eine zweite Bindung. Detail, Liste und CSV führen die Quell-ID; der Zielverlauf schreibt ClonedFromHearing (770).

Vorschau und Anlage verlangen Hearings.Default und .Create. Dateiauswahl/-kopie verlangt zusätzlich Documents.Default, .Download und .Manage; ohne diese Rechte liest die Vorschau keine Dateimetadaten. Die normale Institutions-Vertragsautorisierung gilt zusätzlich. Ein Replay prüft die heutigen Rechte erneut, liefert bei gleichem Auftrag das bestehende Ziel vor Prüfung inzwischen veralteter Stempel und verweigert andere Akteure/Nutzlasten.

Eine neue Kopie reserviert ihren verantwortlichen Ersteller über den bestehenden Authorization-Vertrag IStaffResponsibilityReservation.ReserveCreatorAsync. Aktuelle Kontoeignung und konkurrierende Deaktivierung folgen damit denselben Regeln wie bei der regulären Neuanlage; die Reservierung bleibt Teil der Kopiertransaktion.

Die Serializable-Unit-of-Work hält Quelllesesperren bis zum Commit; sie verändert weder Quellstempel noch Quellhistorie. Dateikopien verwenden den bestehenden transaktionalen Datenbank-BLOB-Vertrag von IDocumentFilingPort; ein anderer Provider wird vor der Kopie abgewiesen. Fehlende Bytes oder Fehler in späteren Teilschritten rollen auch bereits erzeugte Dateien zurück. Individuell widerrufene Grants bleiben ausgenommen; allein durch das Verfahrensende widerrufene Zuordnungen können auf neue IDs abgebildet werden. Pflichtunterlagen verwenden weiterhin die reguläre Zuordnung an alle Zielbeteiligten.

ID-only über zwei Modulgrenzen am Beispiel des Verwendungsnachweises

RALQMFilingHearingUsages (RAL-QM-HP-16) verbindet in einer Zeile fünf Kennungen aus vier Besitzbereichen: SourceEntryId (eigener Slice), SourceDescriptorId (CAF Documents), HearingId und HearingDocumentId (Hearings) sowie AdoptedByUserId (ABP Identity). Alle fünf sind blanke Guid-Spalten ohne Navigation und ohne Fremdschlüssel — die Regel des vorigen Absatzes, hier über zwei Modulgrenzen gleichzeitig.

Die Tabelle führt darüber hinaus keine fremden Fakten: kein Verfahrenskennzeichen, keinen Verfahrenstitel, keinen Status, keinen Dateinamen. Die holt die Abfrage zur Laufzeit über IHearingDocumentAdoptionPort.DescribeAsync und filtert sie dabei nach den Leserechten des Aufrufers. Eine denormalisierte Kopie wäre bequem und würde genau das dauerhaft preisgeben, was §1 zurückhalten soll — und nach einer Umbenennung oder einem Statuswechsel des Verfahrens zusätzlich falsch sein.

Zeilen werden nie gelöscht. Sie sind der fachliche Tombstone aus SPEC-RAL-QM-HP-19: Papierkorb und endgültiges Entfernen betreffen nur die Ablagefassung, der Herkunftsnachweis bleibt in jedem Status auflösbar. Die committete Zeile ist zugleich der Beleg der Operation — der eindeutige Index auf OperationId entscheidet eine wiederholte Übernahme, nicht ein zusätzlicher Zustand im Aggregat.

Data-preserving Renames

Die laufende RALQM-Umbenennung zeigt das verbindliche Muster für Identifier-Änderungen: Die Migration RenamePersistedIdentifiersToRALQM (HearingPortal/Host/Migrations/20260825174437_RenamePersistedIdentifiersToRALQM.cs) benennt Tabellen (z. B. QualityMarksHearings → RALQMHearings, QualityMarksInstitutions → RALQMInstitutions, CafDocumentShareGrants → CAFDocumentShareGrants) und Indizes per RenameTable/RenameIndex um, statt Daten zu löschen und neu anzulegen. Bereits ausgerollte Migrationen behalten dabei ihre historischen Bezeichner (z. B. Constraint-Namen wie FK_GzHearings_… bleiben in älteren Migrationsschritten unverändert); nur der aktuelle Modellstand und neue Migrationen verwenden das neue Präfix. Diese Regel gilt für jede künftige Umbenennung persistierter Kennungen.

Kreis plus Kennzeichen am Beispiel der Pflichtpartner

RAL-QM-HP-10 (O4) trennt bei Institutionen den Kreis (InstitutionType: Fachkreis, Verkehrskreis) vom Pflichtpartner-Kennzeichen (IsMandatoryPartner, eigene Bit-Spalte). Die Migration AddInstitutionMandatoryPartnerFlag legt die Spalte an und stellt jede Bestandszeile mit dem zurückgezogenen Typwert 0 auf Verkehrskreis mit gesetztem Kennzeichen um, auch weich gelöschte Zeilen, damit keine Zeile einen undefinierten Kreis behält, und entfernt danach den von der Spaltenanlage abgeleiteten DEFAULT 0 auf Type (wie RAL-QM-HP-4 bei den Wertelisten-Referenzen): ein roher INSERT ohne Kreis scheitert, statt den zurückgezogenen Wert still zu speichern. Ein Abbruchweg wie bei den Wertelisten fehlt bewusst: die Zeilen bleiben lesbar und in der Maske korrigierbar. Down() stellt den Default wieder her, setzt markierte Zeilen auf den alten Typwert zurück und entfernt die Spalte; für jeden markierten Fachkreis ist das verlustbehaftet und in der Migration dokumentiert. Die Enum-Werte 10 und 20 bleiben der Persistenzvertrag, der Wert 0 bleibt unbelegt und wird vom Aggregat abgewiesen.

Beteiligungsgrund als Pflichtspalte mit Bestandsübernahme (RAL-QM-HP-5)

RAL-QM-HP-5 (AC-3, A2) gibt jeder Beteiligung einen Beteiligungsgrund (HearingParticipationReason: Pflichtpartner 10, manuell 20) als Pflichtspalte ParticipationReason auf RALQMHearingParticipants – voll benannt, weil WithdrawalReason daneben steht und etwas anderes meint. Der Grund folgt dem Pflichtpartner-Kennzeichen der Institution im Moment der Zuordnung und ist danach unveränderlich (E2); die eine Regel dafür, HearingParticipationReasonRule, liegt im Hearings-Contract, damit Einzelzuordnung, automatische Einbindung, Kopie, Nachbeteiligung, Gruppenzuordnung und der Demo-Seed des Hosts sie nicht verschieden ableiten. Das Aggregat weist den unbelegten Wert 0 ab (InvalidParticipationReason). Die Migration AddHearingParticipantReasonAndAccessRevocation folgt dem Muster des Kreises: Sie legt die Spalte mit DEFAULT 0 an, trägt den Bestand im selben Schritt nach – jede Zeile, auch zurückgezogene und weich gelöschte, erhält 10, wenn ihre Institution heute das Kennzeichen trägt, sonst 20; fehlt die Institution, gilt manuell – und entfernt danach den Default, sodass ein roher INSERT ohne Grund scheitert, statt die 0 still zu speichern. Kein Abbruchweg: Die Nachtragsregel lässt keine Zeile ohne Grund zurück. Dieselbe Migration bringt die Spalte AccessRevokedAtUtc (nullable datetime2, UTC) für den gezielten Zugangsentzug aus AC-10, damit die Story genau eine Schemaänderung ausliefert. Down() entfernt beide Spalten; der nachgetragene Grund ist danach verloren, was für die Bestandszeilen (Demo-Daten) hingenommen ist.

Die Spalte AccessRevokedAtUtc ist der Zustand „Zugang entzogen“ je Beteiligung (AC-10, E6): kein zweiter Statusraum, keine eigene Tabelle. Geschrieben wird sie nur vom Aggregat (Hearing.RevokeParticipantAccess, RestoreParticipantAccess) und nur in Dispatched und Adjudication; ein zweiter Entzug (AccessAlreadyRevoked) und eine Freigabe ohne Entzug (AccessNotRevoked) werden abgewiesen, außerhalb des laufenden Verfahrens jeder Wechsel (AccessChangeRequiresRunningHearing) – nach dem Ende bleibt ein entzogener Zugang entzogen. Status, Empfänger, Grants und Fristen der Beteiligung bleiben unberührt; es gibt keine Begründung. Jeder Wechsel erhält einen Verlaufseintrag (AccessRevoked 600, AccessRestored 610, Subjekt die Beteiligung, keine Details); der Katalog bleibt geschlossen und wird nie umnummeriert.

Empfängerstand als Snapshot-Spalten und eingefrorener Versandstand (RAL-QM-HP-6)

RAL-QM-HP-6 trennt zwei Stände, ohne einen zweiten Versandbeleg anzulegen. Der operative Stand einer Beteiligung sind ihre Snapshot-Spalten auf RALQMHearingParticipants: neben Name, Anrede, Anredeform, Titel und E-Mail seit der Migration AddHearingRecipientStand auch InstitutionStreetSnapshot, InstitutionPostalCodeSnapshot, InstitutionCitySnapshot, InstitutionCountrySnapshot (Anzeigename des Landes) und ContactFunctionSnapshot (Anzeigename der Funktion) sowie RecipientStandUpdatedAtUtc als Zeitpunkt des letzten Befehls. Alle neuen Spalten sind nullable und werden nicht aus heutigen Stammdaten befüllt: ein Bestand ohne Anschrift zeigt „nicht festgehalten“, nie einen erfundenen historischen Wert. Der Stand ist ein Textbeleg – Land und Funktion stehen als Namen, nicht als Katalog-Ids, damit eine spätere Umbenennung im Katalog den Beleg nicht umschreibt. Der Versandstand liegt auf der Postausgangszeile RALQMHearingOutboxMessages (RecipientInstitutionName, RecipientStreet, RecipientPostalCode, RecipientCity, RecipientCountry, RecipientSalutation, RecipientTitle, RecipientName, RecipientFunction, dazu das bestehende ToEmail), eingefroren vom Renderer im Moment des Einstellens und danach nie geändert; Zeilen von vor der Story bleiben NULL. Die drei Befehle der Story (Übernahme, Empfängerwechsel, Adresskorrektur) ändern nur die Snapshot-Spalten der Beteiligung, laufen unter dem Verfahrensstempel und schreiben je einen Verlaufseintrag (RecipientStandUpdated 640, RecipientContactChanged 650, RecipientEmailCorrected 660) mit Feldnamen bzw. Kontakt-Ids, nie mit Werten; die Übernahme ist zusätzlich an den ComparisonStamp der Vorschau gebunden (SHA-256 über die aktuellen Stammdatenwerte), damit nie Werte übernommen werden, die niemand gesehen hat. Beide Tabellen bleiben [DisableAuditing] für die Snapshots.

Kontaktmeldung als eigener Datensatz (RAL-QM-HP-41)

RAL-QM-HP-41 legt mit der Migration AddHearingContactReports genau eine Tabelle an, RALQMHearingContactReports, und ändert keine bestehende. HearingContactReport ist ein eigenes Aggregat mit Fremdschlüsseln auf Verfahren und Beteiligung (beide Restrict), nicht Kind des Verfahrensaggregats: Melden und Zurückziehen ändern weder Verfahren noch dessen Stempel. Die Zeile trägt die gemeldete Person, meldendes Konto und UTC-Zeitpunkt, Zustand, Abschlusszeitpunkt und -konto, Ziel, den übernommenen Kontaktbezug, die stattdessen übernommene Person und die interne Notiz. Zustand und Ziel sind durch Check-Constraints begrenzt; ihre Zahlenwerte sind Migrationsvertrag. Der gefilterte eindeutige Index IX_RALQMHearingContactReports_OpenPerParticipant ([State] = 0) lässt je Beteiligung höchstens eine offene Meldung zu; eine Verletzung wird als „bereits offen“ gemeldet. Die Funktion steht als Katalog-Id, damit eine gespeicherte Meldung übernehmbar bleibt; der Empfängerstand erhält bei der Übernahme wie immer den Anzeigenamen.

Die Beteiligung braucht keine neue Spalte: ContactId war schon nullable. Ein Empfänger „aus Kontaktmeldung, nur dieses Verfahren“ ist abgeleitet – kein Kontaktbezug und die jüngste übernommene oder ersetzte Meldung der Beteiligung mit Ziel „nur dieses Verfahren“ (HearingContactReportRules.FindHearingOnlyRecipientReport, ADR-14). Übernehmen und Ersetzen laufen als serialisierbare Unit of Work über beide Bausteine; alle Prüfungen liegen vor dem ersten Speicherschritt, weil ABP die Entity-Änderungen eines erfolgreichen Speicherns auch dann ins Audit-Log schreibt, wenn der Aufruf danach scheitert – wie beim Standardansprechpartner. Ein Deadlock-Opfer wird als Konflikt gemeldet. Die Tabelle ist [DisableAuditing] und liegt außerhalb der Entity History von Hearings; der neue Stammdatenkontakt wird von Institutions wie jede Kontaktpflege protokolliert. Nachweise: HearingContactReportSqlServerTests, InstitutionAuthorizationPipelineTests (einen Kontakt hinzufügen); die Migration läuft im SQL-Fixture der *.SqlServer-Suiten aus leerer Datenbank, has-pending-model-changes prüft das Gate.

Kommunikationsschritt, Begleitdokument und Versandbeleg (RAL-QM-HP-24)

RAL-QM-HP-24 trennt den gebundenen Vorlagensatz (RAL-QM-HP-26, unveränderlich) vom bearbeitbaren Wortlaut: RALQMHearingCommunicationSteps hält je Verfahren, Zweck und Sprache (eindeutiger Index IX_RALQMHearingCommunicationSteps_Hearing_Purpose_Culture, seit RAL-QM-HP-101) Betreff, Text, Herkunft (SourceTemplateId, SourceRevision, SourceCultureName, IsFallback), Initialisierung (Akteur oder null für automatisch), UpdatedAtUtc, ReleasedAtUtc und die Institutionsabweichungen als eine JSON-Spalte (DeviationsJson; kleine Liste je Schritt, nie per SQL gefiltert). Der Schritt ist Kind des Hearing-Aggregats: jede Textänderung bewegt den Verfahrensstempel, eine Einreichung oder ein Versand, der eine Textänderung kreuzt, endet als Konflikt. Text und Abweichungen sind [DisableAuditing]; der Verlauf trägt Zweck und Beteiligung, nie den Wortlaut. RALQMHearingOutboxMessages erhält CoverDocumentId/CoverDocumentFileName (die Datei aus CAF Documents, keine zweite Metadatentabelle), SentSubject/SentBody (die Strings, die der Transport bekam, einmal bei MarkSent, nie umgeschrieben; Altzeilen NULL = „unvollständiger Altbeleg“) und den Bounce-Marker BouncedAtUtc/BouncedAttemptNo/BounceReasonCode; IsUndeliverable ist berechnet (Bounce des letzten Versuchs bei Sent). RALQMHearingOutboxBounces ist das append-only Ingest-Protokoll je Meldung (OutboxMessageId+ExternalReference eindeutig), damit dieselbe Meldung einmal wirkt und ein verspäteter Bounce eines älteren Versuchs sichtbar bleibt, ohne den neueren Erfolg zu überschreiben.

Kommunikationssprache je Empfänger (RAL-QM-HP-101)

RAL-QM-HP-101 macht die Sprache der Verfahrensnachrichten zu einer Eigenschaft des Empfängers, nicht des Bearbeiters (ADR-22):

  • Sprachen. Aktive Sprachen und Standardsprache kommen aus der Sprachverwaltung (ILanguageProvider, Einstellung Abp.Localization.DefaultLanguage); HearingLanguages löst sie zentral auf, Kennungen sind die Kulturnamen der Sprachverwaltung (de-DE, en). Hearings rechnet Kulturnamen nicht um; die Altkennungen de/en stellt der Migrator einmalig um (Inhalte, Sprachstände, gebundene Sätze; strukturierte Ereignisse 10101, 10103–10106) und entfernt dabei wortgleiche Kopien des Standardtexts unter weiteren Sprachen in jeder Vorlage, jede gemeldet (E11), über die einzige Löschoperation des Vorlagenports (IManagedTemplateContentPort.DeleteAsync).
  • Kontakt. InstitutionContact.CommunicationLanguage (optional, 16 Zeichen) wird mit RALQM.Institutions.Update und Institutionsstempel gepflegt; leer, unbekannt oder inaktiv liest sich als Standardsprache (HearingLanguages.ResolveEffectiveAsync); eine gespeicherte, später deaktivierte Sprache bleibt am Kontakt, blockiert kein Speichern der Institution und gilt bis zur Änderung als Standardsprache, der Wechsel auf eine inaktive Sprache wird abgewiesen (E12). Hearings liest sie permissionfrei über IInstitutionContactLanguageReader aus Institutions.Contracts.
  • Sprachstände. RALQMHearingCommunicationSteps ist eindeutig je Verfahren, Zweck und Sprache. Der Editor bietet nur die wirksamen Sprachen der Empfänger an („Anpassen pro Sprache“ wie in der Textvorlagenverwaltung: Bezugssprache lesend links, sichtbar abgesetzt, Zielsprache bearbeitbar rechts, nie dieselbe Sprache; die Zielsprachenwahl nennt die Empfänger je Sprache); ein Speichern nennt die nicht angepassten Sprachen als Hinweis, eine Abweichung eines Empfängers in einer anderen Sprache ebenso; der Editor beginnt in der Standardsprache. Die Empfängerliste für Abweichung und Endfassung zeigt alle Institutionen mit ihrer Sprache; Abweichung und Endfassung folgen der Sprache der gewählten Institution; ungespeicherte Änderungen werden beim Wechsel von Sprache, Institution oder Zweck abgefragt; ein gesperrter Sprachstand vergleicht nur („Sprachen vergleichen“, Felder deaktiviert); die Knöpfe stehen nebeneinander wie überall (RAL-QM-HP-101 E15, E16, E19–E21; Vereinfachung vom 16.10.2026). Eine Sprache ohne gebundene Fassung und ohne eigenen Text spiegelt den jeweils aktuellen Text der Standardsprache (HearingCommunicationMirror): Nachsehen friert nichts ein, erst ein eigener Text oder eine Abweichung legt den Sprachstand an, erst der eigene Text macht ihn eigenständig (E13). Vor der ersten Zeile dieser Sprache entsteht er automatisch aus dem dann aktuellen Standardtext und trägt das Rückfallkennzeichen (IsFallback, SourceCultureName). Der OutputStamp umfasst alle Sprachstände und folgt dem Standardtext, solange eine Sprache ihn spiegelt.
  • Versand. Kein Sendeweg nennt eine Sprache (HearingOutboxEnqueueRequest.CultureName null); HearingOutboxRenderer löst sie je Zeile aus der Kontaktsprache des Haupt- oder gewählten Zusatzempfängers auf (der Sprachleser ist Pflichtparameter), rendert Anrede, Linkzeile, Datenschutzzeile, Zusatzblöcke und den Portalhinweis der Endfassung (HearingLocalizedModelParts) in dieser Sprache und meldet sie; HearingOutboxManager friert CultureName und ContentCultureName auf der Zeile ein, die Vertretungskopie und eine Zustellkorrektur tragen beide. Datumswerte reisen roh (HearingMessageDates, yyyy-MM-dd; nur die Datumsschlüssel des Modells, DateKeys) und werden im Kurzdatum der Zeilensprache geschrieben; der Antwortabsatz der Bescheidung entsteht aus Erwiderung und Votum (HearingAdjudicationReplyBlock); das Begleitschreiben datiert in der Zeilensprache, Grußformel, Beschriftungen, Seitenzahl und PDF-Titel kommen aus deren Ressourcen (HearingCoverLetterTexts), sein Dateiname bleibt als technischer Bezeichner deutsch (E14).
  • Interne Mails (Runde vom 16.10.2026). Freigabe- und Einreichungshinweis, Störungsmeldung, Rückmeldehinweis, Erinnerungsbericht und Abschluss-Erinnerung gehen je Empfänger in dessen Kommunikationssprache hinaus: IStaffNotificationRecipientReader liefert sie (StaffNotificationRecipientDto.CultureName), die Prozessoren rendern je Ziel, Zeitpunkte schreibt HearingMessageDates.FormatInstant wie in den Empfängernachrichten (E26); Berichts- und Abschlussdokumente bleiben in der Standardsprache (E27).
  • Konten (Runde vom 16.10.2026). Ein Mitarbeitendenkonto trägt seine Sprache als Zusatzeigenschaft des ABP-Benutzers (RALQM.Profile.CommunicationLanguage), ein Institutionskonto liest die seines gebundenen Kontakts (AccountCommunicationLanguage). Jede Sprachänderung – Referent am Kontakt, Empfänger oder Mitarbeitender am eigenen Konto, Verwaltung in der Kontoverwaltung – setzt zuerst das locale-Attribut des Anmeldedienstes (IKeycloakAdminClient.SetLocaleAsync) und wird erst dann gespeichert; nimmt der Dienst sie nicht an, wird die ganze Änderung abgewiesen (E23). Institutions meldet Kontaktänderungen vor dem Speichern über IInstitutionContactLanguageSynchronizer; die Selbstbedienung schreibt den gebundenen Kontakt über IInstitutionContactLanguageWriter. Kontomails der Anwendung rendern unter der Kontosprache (HP11-Reset: Snapshot und fester Deutsch-Fallback gemäß ADR-19); der Kontoverlauf hält jede Änderung fest (CommunicationLanguageChanged, E24).
  • Rundnachricht (Runde vom 16.10.2026). HearingCircular trägt neben dem Text der Standardsprache die Texte weiterer Sprachen (TextsJson, ganz oder gar nicht je Sprache, ein wortgleicher Text ist ein Spiegel und wird nicht gespeichert; E28, E35); die angebotenen und beim Anlegen aus der Vorlage vorbelegten Sprachen sind Standardsprache und Empfängersprachen der Gruppe (E34), der Editor folgt /versand (Umschalter, Bezugssprache lesend, Zielsprache mit Spiegel der Standardsprache), ein Text einer deaktivierten Sprache bleibt oder wird entfernt, nie geändert (E36); jede Institution erhält die wirksame Sprache ihres Standardkontakts, ohne eigenen Text den Standardtext mit ContentCultureName an der Zeile (E29); die Empfängerprüfung nennt in einem Satz die Institutionen, die den Standardtext erhalten, Zeilen und Ergebnis tragen kein Kennzeichen, der Postausgang zeigt Sprache und Rückfall der Zeile; nach dem Versand stehen die Texte untereinander, lesend; ein Text einer deaktivierten Sprache ist nur lesbar und entfernbar (E36). Zeitpunkte aller Mails schreibt HearingMessageInstants (Hearings.Contracts), auch die Kontomails von Authorization (E38).
  • Eingangsbestätigung. HearingResponseNotification.CultureName friert die wirksame Sprache des Empfängers beim Speichern des Auftrags ein; der Worker schreibt die Bestätigung in ihr, das Empfangsdatum im Format dieser Sprache.
  • Anzeige. Zustellstatus und Nachrichtendetail nennen die Sprache der Zeile mit dem Anzeigenamen der Sprachverwaltung und den Rückfall im Wortlaut der Vorschau (HearingParticipantDeliveryStatusDto, HearingOutboxMessageDto: CultureName, ContentCultureName, IsFallback, LanguageDisplayName, ContentLanguageDisplayName); der Verlauf nennt die Sprache eines Sprachstand-Eintrags.

Amtliche Abbruchbegründung (RAL-QM-HP-42)

RAL-QM-HP-42 ergänzt RALQMHearings um das Kennzeichen IsCancellationReasonOfficial (Migration AddOfficialCancellationReason, bit, Standard false): true genau dann, wenn Hearing.Abort den Grund gesetzt hat – der Grund eines laufenden Verfahrens ist seit der Story die amtliche, den Institutionen übermittelte Begründung. Stornierungen vor dem Versand (Cancel) und jeder Altbestand behalten false und damit den internen Lesevertrag; eine spätere Veröffentlichung im Portal (RAL-QM-HP-62) darf sich nur auf gekennzeichnete Gründe stützen. Das Kennzeichen ist Prozesszustand, keine protokollierte Datenänderung (HearingHistoryReadModel.ProtocolledProperties unverändert); der Text bleibt wie bisher außerhalb von Verlauf, Audit und Logs. Die Abbruchmitteilung selbst ist eine gewöhnliche Postausgangszeile (CancellationNotice, ProtectedPayload null, Idempotenzschlüssel je Beteiligung); die Begründung liegt im ModelJson unter Begruendung und im gerenderten Text – beide [DisableAuditing] wie jede Zeile.

Archivkennzeichen (RAL-QM-HP-44)

RAL-QM-HP-44 ergänzt RALQMHearings um IsArchived (bit, Standard false, Index IX_RALQMHearings_IsArchived), ArchivedAtUtc und ArchivedByUserId (Migration AddHearingArchiveFlag). Das Kennzeichen ist Prozesszustand, keine protokollierte Datenänderung; der Verlauf trägt Archived (750) und RestoredFromArchive (760) mit Actor und Zeit, ohne Detail. Die Rückholbegründung liegt allein im ABP-Audit-Log des Aufrufs, vollständig als Zusatzeigenschaft ArchiveRestoreReason (die Aktionsparameter leert ABP ab 2000 Zeichen; [DisableAuditing] hält den Grund dort heraus, Review !169), und wird weder am Aggregat noch im Verlauf noch in Logs gespeichert. Kein globaler EF-Filter: jede Listenabfrage nennt das Kennzeichen selbst (Active schließt es aus, Closed verlangt es, All ignoriert es); Historie, Direktzugriffe, Dokumentreferenzen und Retention sehen archivierte Verfahren unverändert. Bestandsverfahren bleiben unarchiviert; ein Endstatus allein ist keine Archiventscheidung.

Veröffentlichungskennzeichen und öffentliche Kennung (RAL-QM-HP-47)

RAL-QM-HP-47 ergänzt RALQMHearings um IsPublished (bit, Standard false, Index IX_RALQMHearings_IsPublished), PublishedAtUtc, PublishedByUserId und PublicId (eindeutig, gefiltert auf gesetzte Werte, Index IX_RALQMHearings_PublicId; Migration AddHearingPublication). Die öffentliche Kennung entsteht beim ersten Kennzeichnen und bleibt auch nach Rücknahme und Ende erhalten; sie ist weder Aktenzeichen noch Verfahrens-Id. Kennzeichen und Sichtbarkeit sind getrennt: kein globaler Filter, jede öffentliche Lesung nennt Kennzeichen und Status (HearingPublicationRule.PublicStatuses) selbst. Die Freigaberücknahme setzt das Kennzeichen zurück (Verlaufseintrag 800), damit ein geänderter und neu freigegebener Entwurf erst nach erneuter Kennzeichnung öffentlich wird. Der Änderungszeitpunkt des Bestands ist der späteste aus der LastModificationTime des Aggregats und der Änderungszeit der referenzierten Einträge von Kennzeichnungsart und Verfahrensart (IValueListLookup.GetChangeTimesAsync), weil der Bestand deren aufgelöste Namen trägt; eine Umbenennung in der Werteliste erscheint so im Delta. Die Namen löst der Abruf immer in de-DE auf (SQL-Nachweise in der Spec). Entfernungen sind Verfahren mit Kennung, die nicht mehr sichtbar sind; sie verlassen das Portal nur als Kennung mit Zeitpunkt.

Votum, Fassungskette und Bescheidungsstand je Beteiligung (RAL-QM-HP-46)

RAL-QM-HP-46 ergänzt drei Tabellen (Migration AddAdjudicationVoteAndPartialDispatch):

  • RALQMHearingResponseReplies: Vote (int, nullbar; 10 Bestätigung, 20 Ablehnung) und PreviousReplyId (die ersetzte Fassung). Der eindeutige Index UX_RALQMHearingResponseReplies_HearingResponseId_PreviousReplyId ist nicht gefiltert: SQL Server behandelt NULL in einem eindeutigen Index als Wert, eine Stellungnahme hat deshalb genau eine Erstfassung und jede Fassung höchstens einen Nachfolger. Die Fassungen bilden eine Kette; zwei parallele Korrekturen derselben Fassung können nicht beide gespeichert werden. Der Dienst prüft die gelesene Fassung zuerst selbst und wertet nur die Verletzung dieses Index als denselben Konflikt (AdjudicationReplyConflict). Der Index ersetzt den einfachen Index auf HearingResponseId, dessen führende Spalte er teilt. Antworten bleiben append-only.
  • RALQMHearingParticipants: AdjudicationDispatchedAtUtc und AdjudicationReplyId – wann die Beteiligung ihre Bescheidung erhielt und mit welcher Fassung. Beide „first write wins“; die Fassung ist eine schlichte Kennung ohne Fremdschlüssel, wie der Verweis der Antwort auf ihre Abgabe.
  • RALQMHearings: AdjudicationFullyDispatchedAtUtc – der Zeitpunkt der vollständigen Übermittlung. AdjudicationDispatchedAtUtc bleibt der Erstversand und sperrt Variante und Komposition; Abschlusserinnerung (RAL-QM-HP-43) und Abschlussvoraussetzung (RAL-QM-HP-51) lesen den neuen Zeitpunkt.

Sichtbarkeit ist nie ein eigener Wert: Portal und Rückmeldungsübersicht leiten den übermittelten Stand aus dem Stand der Beteiligung ab (HearingPortalRules.DispatchedReplies) – je Abgabe die Fassung, die bei der Übermittlung an diese Beteiligung wirksam war.

Bestandsübernahme ohne Erfindung: Keine Bestandsantwort erhält ein Votum. Die vorhandenen Antworten einer Stellungnahme werden in der Reihenfolge der Regel für die wirksame Fassung verkettet (Antwortzeitpunkt, dann Kennung als Kleinbuchstaben-Hex ohne Bindestriche, ordinal), bevor der eindeutige Index entsteht. Jedes versandte Verfahren gilt zu seinem Versandzeitpunkt als vollständig übermittelt, weil ein Versand bisher immer die eine Welle an alle war. Bedient ist, wer einen Bescheidauftrag im Postausgang hat; seine Fassung ist die damals wirksame seiner letzten Abgabe. Eine früher ohne Antwort bediente Beteiligung behält den Zeitpunkt und nennt keine Fassung.

Dateiinhalt nach gescheiterter Unit of Work (RAL-QM-HP-51)

Der Datenbank-BLOB-Provider speichert Inhalt in derselben Transaktion wie den Deskriptor; ein Rollback nimmt beides zurück. Der optionale Dateisystem-Provider schreibt die Datei sofort. Für Erstellungen über IDocumentContentPort.CreateAsync (Abschlussbericht, Begleitbriefe, Anlagen einer Stellungnahme, Verfahrenskopie) räumt deshalb der Documents-Baustein selbst nach, ohne Port- oder Konsumentenänderung: Er merkt jede neue Inhalts-Id an der laufenden Unit of Work vor; scheitert sie, prüft er nach dem Rollback in einer neuen Unit of Work, welche Deskriptoren committet sind, und löscht nur die übrigen Dateien über den Blobnamen. Ein Konsument kann das nicht leisten: nach dem Rollback fehlt der Deskriptor, über den er löschen dürfte, und vor dem Rollback blockiert oder scheitert jeder weitere Befehl derselben Transaktion. Grenzen und Zusicherung: Documents-Vertrag. Seit RAL-QM-HP-81 prüft die Bereinigung den Deskriptor ausdrücklich unter ReadCommitted, ihre Zeitgrenze ist Konfiguration (CAFDocuments:Storage:RollbackCleanupTimeLimit), und ein wiederkehrender Abgleichslauf entfernt beim Dateisystem-Provider jede Datei ohne committeten Deskriptor, die älter als die Mindestzeit ist (CAFDocuments:Storage:Reconciliation) – auch nach einem Prozessabbruch. Ein Löschfehler ist ein echter Rest und wird als Fehler mit Inhalts-Id und Fehlerklasse gemeldet; eine gescheiterte Kompensation eines Konsumenten ist nur eine Warnung (Risiken).

Rückläufer und Unzustellbarkeit (RAL-QM-HP-76, ADR-17)

Jeder Versandversuch trägt eine signierte Message-ID (HearingMessageIdCodec, HMAC über Zeile und Versuch); mit Hearings:DeliveryReports:ReturnAddress gehen Mails mit dieser Rücklaufadresse hinaus. HearingDeliveryReportWorker liest das Rückläuferpostfach per IMAP, HearingDeliveryReportProcessor ordnet eine RFC-3464-Meldung mit Action: failed genau dann zu, wenn sie genau eine gültig signierte Referenz nennt - über HearingBounceRecorder, dieselbe Regel wie der interne Erfassungsweg (Ergebnis je Versuch, Verlauf DeliveryBounced, Störung nur für den aktuellen Versuch, Dubletten über die Meldungsreferenz). Alles andere landet ohne Text in der Klärungsliste /unzustellbarkeiten (Recht RALQM.Hearings.DeliveryReports.Review), wo es nur mit Begründung erledigt werden kann. Die Empfängeradresse einer Meldung wird nie zur Zuordnung benutzt.

Schadsoftwareprüfung hochgeladener Dateien (RAL-QM-HP-50, ADR-16)

RAL-QM-HP-50 ergänzt CAF Documents um einen Prüfstand je Inhalt und einen dauerhaften Prüfauftrag. Der Stand liegt als ExtraProperty CAF.Modules.Documents:ScanState am ABP-Dateideskriptor; fehlt er (Altbestand), gilt die Datei als „in Prüfung“. Die Tabelle CAFDocumentScanJobs (Migration AddDocumentScanJobs, Schlüssel = Dokument-Id) hält Herkunft, Versuchsnummer, Anspruch, nächsten Versuch, Beginn des Fehlerfensters, Fehlercode und Signaturname. Index (State, NextAttemptAtUtc). Deskriptor und Auftrag werden in derselben Unit of Work geschrieben.

Ablauf und Regeln: - Lesen: OpenContentAsync und der Share-Link liefern nur unbedenklichen Inhalt. - Upload: wird sofort geprüft (InlineTimeout, 10 s). Bei Befall wird er abgewiesen (ContentInfected mit Dateiname); ohne Ergebnis wird er gesperrt gespeichert. - Worker: Anspruch je Versuch, Übernahme nach StuckAttemptTimeout, Ergebnis nur für den laufenden Versuch. Fehler werden mit 1 min, verdoppelt, höchstens 1 h wiederholt, nach 24 h ist die Prüfung Failed. Uploads gehen vor Altbestand; den Bestand reiht der Worker seitenweise ein, die neuesten Dateien zuerst (die der laufenden Verfahren). Standarddurchsatz rund 300 Dateien je Minute. - Ergebnisse: Ein Endstand wird als DocumentScanStateChangedEto veröffentlicht. Hearings macht daraus Störungen der Quelle DocumentScan. - Ausnahmen: Kopien erben den unbedenklichen Befund. Systemerzeugte PDFs werden nicht geprüft.

Individuelle Frist und korrigierter Zustellauftrag (Audit 29.09.2026)

Die Migration AuditRedeliveryAndParticipantDeadlines ergänzt ausschließlich Hearings-eigene Tabellen. An der Beteiligung stehen die nullable UTC-Frist und Begründung sowie ein Revisionszähler mit Ausgangswert 0. Die Begründung bleibt aus dem generischen Audit ausgeschlossen. ExtendDeadlineAsync serialisiert am Verfahren und prüft dessen Stempel; Frist, abgeleiteter Beteiligungsstand, neue Fristaktionen und Verlauf werden gemeinsam gespeichert. Auch eine spätere zweite Verfahrensfrist erzeugt für betroffene individuelle Fristen eine neue Planungsrevision, wenn sich deren wirksamer Tag verschiebt; terminale alte Jobs bleiben erhalten.

Die Postausgangszeile hält RedeliverySourceId, RedeliveryOperationId, SupersededByMessageId und RedeliveryCompletedAtUtc. Gefilterte Unique-Indizes auf Quelle und Operation verhindern mehrere Nachfolger oder mehrdeutige Wiederholungen. Der alte Auftrag behält Empfängerstand, Text und Versuchshistorie; ein neuer Auftrag erhält nur die ausdrücklich bestätigte operative E-Mail als neue Transportadresse. Es entstehen keine modulübergreifenden Navigationen oder Fremdschlüssel. Die Referenz auf den erfolgreichen Nachfolger ersetzt keinen eigenen SMTP-Versuch des Vorgängers und setzt dessen SentAtUtc nicht. Nach einem Abbruch zwischen Nachfolgererfolg und Vorgängerabgleich ergänzt der periodische Incident-Detector fehlende Erledigungszeitpunkte anhand reziproker Links desselben Verfahrens und derselben Beteiligung. Höchstens 500 passende Zeilen je Durchlauf halten den Abgleich begrenzt; weitere Durchläufe vervollständigen die Kette ohne erneuten Versand.

Standardansprechpartner als Kennzeichen mit gefiltertem eindeutigem Index (RAL-QM-HP-9)

RAL-QM-HP-9 (AC-2, A6) macht den Standardansprechpartner zu einer eigenen Spalte IsDefault (bit NOT NULL) auf RALQMInstitutionContacts. Geschrieben wird sie nur vom Aggregat Institution (SetDefaultContact tauscht alten und neuen Standard in einem Aufruf); die Invariante „höchstens ein Standard je Institution“ sichert zusätzlich der gefilterte eindeutige Index IX_RALQMInstitutionContacts_DefaultPerInstitution über InstitutionId mit dem Filter [IsDefault] = 1 AND [IsDeleted] = 0. Der Schlüssel ist bewusst InstitutionId allein und nicht (InstitutionId, IsDefault): Unter dem Filter ist das Kennzeichen konstant, und im Schlüssel würde EF für die zwei Zeilen eines Wechsels Ordnungskanten über den eindeutigen Wert ableiten, die in einem Zirkelfehler enden. Einen Wechsel in einem SaveChanges liefert keine Indexform: EF setzt die beiden UPDATEs einer Tabelle in Primärschlüsselreihenfolge ab, und SQL Server prüft den gefilterten Index je Anweisung; sortiert der neue Standard zuerst, scheitert seine Anweisung am noch gesetzten alten Kennzeichen. Ein Standardwechsel wird deshalb in zwei Speicherschritten innerhalb einer Transaktion persistiert (Institution.ClearDefaultContact, speichern, SetDefaultContact, speichern); ein Zwischenstand ohne Standard ist für andere Verbindungen nicht sichtbar. InstitutionSqlServerTests weist beide Schlüsselreihenfolgen nach und hält den scheiternden Einzelschritt fest. Beide Speicherschritte stehen am Ende von InstitutionAppService.UpdateAsync; der Wechsel selbst und alle fachlichen Prüfungen laufen vorher im Speicher. Das hat einen Grund über die Ordnung hinaus: ABP hängt die Entity Changes jedes erfolgreichen SaveChanges sofort an das Audit-Log des Aufrufs, und dieses Audit-Log wird auch bei einem gescheiterten Aufruf gespeichert. Eine fachliche Abweisung nach einem Speicherschritt rollt die Fachdaten zurück, hinterließe aber Einträge in der Entity History für Änderungen, die nie galten (nachgewiesen und festgehalten durch InstitutionAuditEntityHistorySentinelTests.A_refused_update_leaves_no_entity_history_of_the_rolled_back_swap). Ein technischer Fehler im zweiten Speicherschritt, etwa eine Zeitüberschreitung, ist davon nicht erfasst: Dann stehen die Änderungen des ersten Schritts im Protokoll, obwohl sie zurückgerollt wurden. Das gilt für jeden Anwendungsfall mit mehreren Speicherschritten in einer Unit of Work. Weich gelöschte Zeilen bleiben durch den Filter außen vor. Der bestehende Index über (InstitutionId, SortOrder) bleibt unverändert.

Die Migration AddInstitutionContactDefaultPhoneDeputy legt die Spalten an und übernimmt den Bestand nach A6, bevor sie den gefilterten Index erzeugt: Je Institution ohne gesetzten Standard wird der bisher abgeleitete Standard gekennzeichnet — der erste aktive, nicht gelöschte Kontakt nach Reihenfolge, Nachname, Vorname, mit der ID als deterministischem Gleichstandsentscheid. Institutionen ohne aktiven Kontakt erhalten keinen Standard; weich gelöschte Zeilen gewinnen nie. Das UPDATE ist wiederholbar, weil es nur greift, wo noch kein Standard steht. Down() entfernt Index, Fremdschlüssel und Spalten.

Dieselbe Migration bringt die Telefonnummer Phone (nvarchar(64) NULL, A2: keine Formatprüfung, kein Index) und die drei Vertretungsspalten aus AC-7: DeputyContactId als Selbstbezug auf RALQMInstitutionContacts — innerhalb des Moduls und deshalb, anders als die ID-only-Spalten über Modulgrenzen, ein echter Fremdschlüssel mit DeleteBehavior.Restrict. Er greift nur bei harten Löschungen (HardDeleteAsync, Purge): Kontakte werden weich gelöscht, RemoveContact setzt IsDeleted, und die Regel aus A5, dass ein als Vertretung eingetragener Kontakt nicht entfernt werden kann, ist eine Aggregatregel in Institution.RemoveContact — sowie DeputyFromUtc und DeputyUntilUtc als nullable datetime2 in UTC (A10; offenes Ende = NULL). Sie sind bewusst DateTime wie die Fristen in HearingDeadlineRule, deren Kalenderdatumsvergleich A10 vorschreibt, und keine DateOnly-Spalten nach C-07, damit die Story keine zweite Zeitsemantik einführt. Die Entity History (RALQMInstitutionsEntityHistory) erfasst die neuen Spalten wie die übrigen Kontaktfelder; anders als bei der internen Notiz ist nichts ausgenommen.

Kontakte entfernen als Soft Delete (RAL-QM-HP-9)

RAL-QM-HP-9 (A5) entfernt Kontakte als Soft Delete: Institution.RemoveContact setzt über InstitutionContact.MarkAsRemoved das ABP-Kennzeichen IsDeleted und lässt den Kontakt in der Sammlung des Aggregats. Herausgenommen wäre er ein verwaister Pflichtabhängiger, den EF Core unter DeleteBehavior.Restrict nicht löschen darf; der Aufruf scheiterte, bevor ABPs Soft Delete greifen konnte („association … has been severed“). Die Beziehung bleibt deshalb Restrict, ohne Modelländerung und ohne Migration. Das Speichern schreibt ein UPDATE der Zeile, ABP setzt DeletionTime und DeleterId und führt die Änderung in der Entity History als Löschung.

Institution.Contacts und jede Kontaktregel des Aggregats blenden entfernte Kontakte aus; nur die Prüfung auf doppelte Kontakt-IDs sieht alle Zeilen. Spätere Ladevorgänge und EF-Abfragen über die Navigation hält ABPs Query-Filter frei.

Eine entfernte Zeile ändert nur IsDeleted. Der Grund ist eine Eigenheit von ABP 10.6: Leert der Speichervorgang, der die Zeile löscht, auch den selbstbezogenen Schlüssel DeputyContactId, erkennt EF Core diesen als erste Änderung; ABP hält die Zeile dann für eine reine Fremdschlüssel-Änderung und setzt weder DeletionTime noch DeleterId. Deshalb leert das Entfernen den Vertreter-Eintrag nicht. Werden ein Kontakt und seine Vertretung oder zwei einander vertretende Kontakte im selben Aufruf entfernt, zählen ihre Einträge für die Regel aus A5 nicht, in jeder Reihenfolge; die Einträge bleiben wirkungslos an den entfernten Zeilen stehen. Ein Vertreter, der für einen bleibenden Kontakt eingetragen ist, bleibt unentfernbar.

Nachweis auf SQL Server: InstitutionAuthorizationPipelineTests (Removing_*, A_removed_contact_is_no_longer_found_by_the_deputy_reader) und InstitutionAuditEntityHistorySentinelTests.Removing_a_contact_is_recorded_in_the_entity_history.

Stellungnahme in einem Aufruf (RAL-QM-HP-38)

RAL-QM-HP-38 speichert Antwort, Anlagen, Verlauf und Beteiligungsstand einer Stellungnahme in einer Serializable-Transaktion, die erst nach dem vollständigen Lesen der Dateien beginnt (Ablauf in Laufzeitsicht §6.4). Drei Persistenzregeln folgen daraus:

  • Reservierung und eindeutige Folge. Seit RAL-QM-HP-40 reserviert der Portal-Schreibweg zuerst die Teilnehmerzeile, bevor er vorhandene Antworten und die nächste Sequenz liest. Dadurch werden zwei Schreiber derselben Teilnahme geordnet. Erstabgabe und interne Erfassung verlangen eine leere Folge; eine Ergänzung verlangt einen Vorgänger. Der frühere Einzelantwortindex wird auf Sequenz 1 begrenzt und schützt damit auch die interne Erstaufnahme. Die positive Sequenz und die Auftragskennung bleiben eindeutig. Dieselbe Kennung mit gleichem Akteur und Inhalt liefert die schon gespeicherte Abgabe, bei Abweichung einen Operationskonflikt. Die vollständige Zugangs-, Status- und Fristprüfung bleibt auch vor der Wiederholung erforderlich. Historische Antworten ohne Auftragskennung erlauben keine behauptete Wiederholung.
  • Wiederholung bei Deadlock. Die Speicherphase läuft in einer eigenen Unit of Work (HearingPortalWriteTransaction, requiresNew). Wählt SQL Server sie als Opfer (Fehler 1205 in der Ausnahmekette, die EF Core in DbUpdateException und zuweilen InvalidOperationException hüllt – SqlServerDeadlockVictim), wiederholt HearingResponseStorageRetry sie bis zu zweimal mit den bereits gelesenen Dateien; kein anderer Fehler wird wiederholt, und die interne Erfassung wiederholt nichts. Die verbleibenden Sperren, die eine Wiederholung nötig machen, stehen in Risiken.
  • Festschreibung statt Anlagenänderung. Neue Dateien dürfen nur während der atomaren Erstellung ihrem neuen Beitrag zugeordnet werden. Nach dessen Festschreibung werden Änderungen, zusätzliche Anlagen sowie weiche und harte Löschung auf dem Persistenzpfad abgewiesen. Das gilt auch für interne Aufrufer. Bescheidtexte bleiben eigene Objekte. Frühere weich gelöschte Zuordnungen werden weder entfernt noch wiederhergestellt; ihre Dateien bleiben unter dem Retention-Schutz und über den Portal-Abruf unerreichbar.

Nachweise auf SQL Server: HearingResponseSubmissionSqlServerTests (ein Aufruf, Rollback, Grenzen, fremde IDs, unveränderte Vorgänger, Sequenzvergabe und Auftragswiederholung, erzwungene Blockade mit Wiederholung) und HearingResponseKindMigrationSqlServerTests (umgeschriebene Kategoriewerte).

Eingangsbestätigung und Referentenhinweis (RAL-QM-HP-35)

RAL-QM-HP-35 ergänzt RALQMHearingResponseAttachments um Sha256 (nchar(64), Kleinbuchstaben-Hex der übernommenen Bytes, NULL für Altanlagen – keine nachträgliche Prüfsumme) und führt RALQMHearingResponseNotifications ein (Migration AddHearingResponseNotifications): je Antwort und Empfängerart (Institution 10, ResponsibleCaseOfficer 20) ein Auftrag, eindeutig über den gefilterten Index IX_RALQMHearingResponseNotifications_Response_Audience solange er kein Folgeauftrag ist, Folgeaufträge eindeutig über PreviousNotificationId. Empfänger werden beim Anlegen eingefroren (operative Kontaktadresse nach RAL-QM-HP-6, zuständiger Referent nach RAL-QM-HP-8). Lebenszyklus wie der Freigabe-Benachrichtigungsauftrag aus RAL-QM-HP-25: Anspruch je Versuch, Wiederholung nach HearingIncidentOptions.NotificationRetrySeconds, Störung unter IncidentSource.Notification, Pause nach 72 Stunden ohne erreichbaren Empfänger, Fortsetzen nur als verknüpfter Folgeauftrag. Die Wartephase eines Versands ist wie im Postausgang auf StuckAttemptTimeout begrenzt: Ein nicht antwortender Transport bleibt mit Versuchsnummer beobachtet, der Lauf geht weiter, ein spätes Ergebnis gilt nur für denselben Versuch, nach einer zweiten Frist wird übernommen. Bewusst kein Postausgangs-Zweck: keine Vorlagenbindung je Verfahren, kein PDF-Begleitschreiben, keine Vertretungskopie. „Wiederholen“ auf der Störungsseite (Recht Incidents.Retry) stellt einen laufenden Auftrag sofort fällig oder setzt einen pausierten an die aktuelle Kontaktadresse bzw. den aktuell Zuständigen fort.

Institutionsgruppen: Name, Mitgliedschaft und Revisionszähler (RAL-QM-HP-3)

RAL-QM-HP-3 legt die Tabellen RALQMInstitutionGroups und RALQMInstitutionGroupMembers an. Drei Festlegungen weichen bewusst vom Bestand ab:

  • Der Name ist eindeutig, nicht nur gewarnt. Verglichen wird mit derselben Regel wie bei den Institutionen (InstitutionNameDuplicateRule.Normalize: ohne Leerzeichen, ohne Beachtung der Groß-/Kleinschreibung), aber als gespeicherte Spalte NormalizedName, die das Aggregat beim Benennen mitführt, und mit dem gefilterten eindeutigen Index IX_RALQMInstitutionGroups_NameUnique auf [IsDeleted] = 0 — Vorbild ist der Standardansprechpartner. Ein Index auf dem Namen allein erkennt „Nord West“ und „NordWest“ nicht als gleich. Nach dem Auflösen ist der Name wieder frei. Die Datenbank vergleicht unter der Spaltensortierung und hält zusätzlich „ß“ und „ss“ für gleich, der Code nicht; beides ist mit eigenen SQL-Tests festgenagelt (InstitutionGroupSqlServerTests).
  • Die Mitgliedschaft wird hart gelöscht. InstitutionGroupMember ist eine reine Entity<Guid> ohne Auditfelder, die Beziehung zur Gruppe steht auf Cascade — nur so darf EF Core die aus der Sammlung genommene Zeile löschen. Beim Auflösen fallen beide Schritte in einen Speichervorgang: Die Mitgliedschaften verlassen die Sammlung und werden gelöscht, die Gruppe wird weich gelöscht. Dass die Entity History danach die Löschung der Gruppe und je Mitgliedschaft einen Eintrag mit Gruppe und Institution enthält, hält InstitutionGroupAuditEntityHistorySentinelTests.Dissolving_records_the_group_deletion_and_every_membership_with_its_institution gegen die migrierte Datenbank fest — das ist die Bedingung, unter der das harte Löschen zulässig ist. Das ist die Gegenrichtung zu Kontakte entfernen und erlaubt, weil die Zeile außer den zwei Verweisen keinen Inhalt trägt: Gruppe und Mitgliedschaft stehen im Selektor RALQMInstitutionsEntityHistory, der beim harten Löschen die Werte mitschreibt. Die Institution wird als echter Fremdschlüssel mit Restrict referenziert (modulintern); weil Institutionen weich gelöscht werden, behält eine gelöschte Institution ihre Mitgliedschaftszeile und fällt nur aus den Lesewegen.
  • Ein Revisionszähler macht Mitgliederänderungen an der Gruppe sichtbar. MutationRevision (Muster FilingArea.MutationRevision) wird bei jeder wirksamen Zuordnung und Entfernung erhöht, damit die Gruppenzeile mitgeschrieben wird und ABP ihren Concurrency-Stamp erneuert. Ohne den Zähler bleibt die Gruppe beim Einfügen einer Kindzeile Unchanged — gemessen in InstitutionGroupSqlServerTests.A_member_change_writes_the_group_row_so_that_abp_can_renew_its_stamp; dass ABP den Stempel dann tatsächlich erneuert, hält InstitutionGroupAuthorizationPipelineTests.A_member_change_renews_the_stamp_on_the_real_abp_path auf dem echten Aufrufweg fest. Das ist die Voraussetzung dafür, dass eine offene Bestätigung zum Auflösen scheitert, wenn inzwischen Mitglieder geändert wurden.

Expand/Contract am Beispiel der Wertelisten

Der Umbau von Enum und Freitext auf Katalogtabellen (ADR-07) ist das Muster für jede Umstellung, die Daten neu verankert: erst erweitern, dann übertragen, in einem späteren Schritt entfernen.

  1. AddValueLists legt die vier Tabellen RALQMMarkTypes, RALQMProcedureTypes, RALQMCountries und RALQMContactFunctions an, ergänzt die vier Id-Spalten an RALQMHearings, RALQMInstitutions und RALQMInstitutionContacts, spielt den Katalog ein und überträgt den Altbestand: Enum-Werte werden auf feste Seed-Kennungen abgebildet, Freitext-Länder per normalisiertem Namensabgleich zugeordnet (nicht Zuordenbares wird als inaktive Ausprägung angelegt, statt es zu verlieren), Positionstexte werden zu Ausprägungen der Funktionsliste. Im selben Schritt wird der globale Setting-Wert der zweiten Rückmeldefrist von „28 Tage nach der ersten“ auf den kumulativen Wert umgerechnet. Die alten Spalten bleiben dabei stehen.
  2. Erst spätere Migrationen entfernen sie: DropHearingLegacyTypeColumns die beiden int-Spalten auf RALQMHearings, DropInstitutionLegacyTextColumns die Freitextspalten Country und Position.

Alle drei Migrationen prüfen, ob noch eine Zeile ohne Wertelisten-Bezug übrig ist, und brechen in diesem Fall mit THROW 51660 innerhalb der Migrationstransaktion ab — bei den beiden Entfernungsschritten vor dem DROP COLUMN, sodass kein nicht abgebildeter Wert vernichtet wird, sondern der Rollout stehen bleibt. Die Datenübertragung ist bis auf eine Ausnahme wiederholbar geschrieben (Einfügungen IF NOT EXISTS, Backfills nur auf noch unbesetzten Zeilen): Die Setting-Umrechnung ist eine Rechnung und würde bei einem zweiten Lauf doppelt addieren; sie ist allein durch __EFMigrationsHistory geschützt und in der Migration entsprechend vermerkt.

Die Seed-Kennungen dieser Migration sind ein Vertrag (ValueListSeedIds) — Demo-Daten und Tests referenzieren sie; ein geänderter Wert bricht sie und entkoppelt Bestandszeilen von ihrem Katalogeintrag. Wertelistenpflege ist außerdem in der ABP Entity History erfasst (RALQMValueListsEntityHistory), damit nachvollziehbar bleibt, wer eine Ausprägung umbenannt oder deaktiviert hat.

Fassungskatalog und Zustimmungsnachweise (RAL-QM-HP-39)

RALQMConsentDocumentVersions und RALQMConsentProofs sind reine Einfügetabellen; ein SaveChanges-Schutz weist Ändern und Löschen beider Typen ab. Die Fassungen tragen je Art eine laufende Nummer mit eindeutigem Index (Art, Nummer). Gültig zum Zeitpunkt T ist die Fassung mit der höchsten Nummer unter allen mit „gültig ab ≤ T“; Rückdatieren ist ausgeschlossen, deshalb ändert eine neue Veröffentlichung nie, welche Fassung zu einem vergangenen Zeitpunkt galt. Veröffentlicht wird in einer serialisierbaren Transaktion gegen die zuletzt gesehene Nummer; eine veraltete Ansicht oder ein gleichzeitiger Publisher endet im Konflikt statt in zwei gültigen Fassungen. Nachweise sind über gefilterte eindeutige Indizes begrenzt: je Konto ein Aktivierungsnachweis ([Purpose] = 1), je Abgabe ein Nachweis ([SubjectId] IS NOT NULL). Der Abgabenachweis wird im Abgabeabschluss von Hearings in die umgebende Unit of Work geschrieben, also in dieselbe serialisierbare Transaktion wie Antwort und Verlauf. Über die Modulgrenze stehen nur Kennungen (UserId, HearingResponseId), keine Foreign Keys.

Speicherinventar und Wiederanlauf (RAL-QM-HP-65)

Filing besitzt FilingStorageContextState, FilingStorageInventoryRun, FilingStorageInventoryItem und FilingStorageFinding. Die Tabellen halten ausschließlich Generation, Revisionen, Cursor, technische IDs, Längen und Befundkategorien; keine aktuellen Dateinamen, Pfade, Eigentümer oder Inhalte. CAF Documents liefert über den lesenden IDocumentStorageInventoryPort Descriptor- und Inhaltsseiten des eigenen Containers. Hearings inventarisiert seine aktiven, entfernten und historischen Unterlagen, Antworten, Postausgangsdateien und Rundnachrichten über IHearingStorageReferenceInventory; Filing liest keine Hearings- oder CAF-Tabelle.

IHearingStorageTrashFacts liefert der unabhängig autorisierten Speicheradministration zusätzlich höchstens 500 technische Papierkorbfakten je Cursorseite. Hearings entscheidet Verfahrens-, Referenz-, Grants- und native Ordnerschutzregeln selbst; ausgegeben werden nur Dokumentkennung, Fristbasis, Schutzende, Beobachtungsstand und Schutz-/Auftragskennzeichen. Namen, Pfade, Eigentümer und Verfahrenskennungen verlassen diesen Port nicht. Cursorseiten sind lesende Beobachtungen; sie erteilen weder Fachzugriff noch eine Löschfreigabe.

Der regelmäßige Worker und der manuelle Start verwenden denselben Runner. Jeder Aufruf schreibt höchstens eine Portion von 128 Ausgangszeilen, aktualisiert die persistierten Cursor und hält den externen Inspektionslease bis zum SQL-Commit. Standardintervall ist 24 Stunden (Filing:StorageInventory:Interval, validiert 1 Minute bis 30 Tage); laufende Portionen werden alle zehn Sekunden fortgesetzt. Inhaltsrevisionen und Längen werden in einem zweiten Durchgang nachgelesen, anschließend die Descriptoren. Mutationen erhöhen in ihrer bestehenden UoW die Kontextrevision; ein dabei veralteter Lauf wird entwertet. Ohne aktivierte externe Generation und durchgängige Mutationsüberwachung gibt es keinen aktuellen Vollerfolg.

Auch die lesende Fortschrittsanzeige vergleicht die externe Generation vor und nach ihren SQL-Lesungen mit Run und Kontext. Ein früherer vollständiger Lauf bleibt nach Rotation, fehlendem Aktivierungsmarker oder defektem Zustand sichtbar, gilt aber nicht mehr als aktuell. Die Beobachtung erwirbt keine weitere Sperre und funktioniert unter bereits gehaltenem Mutations- oder Inspektionslease; sie erteilt keine Freigabe oder Löschberechtigung.

Auch ein mit Enabled=false gestarteter Host hält bei konfiguriertem absolutem Zustandspfad den externen .lock bis zum Ende seiner Mutation. Die Erstinitialisierung wartet auf dieselbe Sperre und persistiert darunter zuerst den dauerhaften .activated- Marker, dann die gesperrte Generation. Bereits gestartete deaktivierte Hosts weisen danach Mutation, Inspektion und Zustandslesen ab; Zustand oder Aktivierungsmarker allein genügen. Eine bloße .lock-Datei ist keine Aktivierung. Ein fehlender Zustand bei vorhandenem Marker bleibt gesperrt und darf nicht mit einer neuen leeren Generation überschrieben werden. Nur eine vollständig unkonfigurierte Modulinstanz ohne Zustandspfad verwendet einen No-op-Lease.

Globale unprüfbare Bestände sperren den Kontext, eingrenzbare Befunde nur die betroffenen Löschungen. Ein neuer vollständiger Abgleich kann frühere Befunde ersetzen; Quittieren oder Neuladen hebt keine Sperre auf. Fehlende Bytes gelten nur bei passendem eigenen Purge-Operations- und Historiennachweis als erwartete Entfernung. Nach betrieblicher Wiederherstellung bleibt die außerhalb der Datenbank geführte Generation maßgeblich. Freigabe verlangt einen aktuellen vollständigen Lauf ohne ungeprüfte Abdeckung oder offene Befunde und bindet dessen Revision. Vor Löschauftrag und Vollzug liest der DeletionGuard die ausgewählten Descriptoren und tatsächlichen Inhalte zweimal unter der vorhandenen Mutationslease. Fehlende/mehrfache Zuordnung, falsche Länge, fremde Speicheridentität oder ein Revisionswechsel blockieren den Aufruf. Ein dabei neu entdeckter Befund wird vor der Abweisung in einer eigenen Transaktion festgeschrieben, ohne die bereits gesperrte Kontextzeile zu lesen oder zu verändern. Der Fachrollback entfernt diesen Befund nicht. Vor dieser Probe hält die äußere Transaktion keine Befund-Lesesperre; die globale Eintrittsprüfung schließt ihre eigene lesende UoW ab. Ein unbekanntes historisches Schutzende ist kein Inhaltsfehler: Die Fristpolicy verlangt dafür ihre konservative volle neue Beobachtungsfrist. Eine leere Schutzrevision bedeutet hingegen eine nicht verifizierbare Beobachtung und erlaubt weder Vollerfolg noch Löschung. Prüflauflesen, Start und Freigabe sind getrennte Produktadministrationsrechte unter RALQM.Filing.Storage; die Seite liegt unter /administration/speicher/prueflaeufe.

AC13 protokolliert den ausdrücklichen Befundabruf über ABPs AlwaysLogSelectors: Die Auswahl bindet exakt FilingStorageInventoryAppService.GetFindingsAsync, keine fremde Serviceidentität oder allgemeine Get-Namensregel. Der native Interceptor erzeugt bei Bedarf seinen eigenen Audit-Scope und übergibt Akteur, UTC, Aktion und technische Run-/Filter-/ Pagingparameter dem AuditStore. Ergebnisse, private Namen, Pfade und Inhaltsbytes sind keine Auditparameter; ABP ersetzt den CancellationToken-Wert bereits durch null. Start, Fortsetzung und Freigabe behalten ihr natives Schreibaktionsaudit; der dauerhafte Freigabeakteur steht zusätzlich am Run. Häufiges GetProgressAsync-Polling bleibt unter der unveränderten GET-Policy und erzeugt keinen Auditdatensatz je Poll. Autorisierung und frische Eignung werden dadurch weder ersetzt noch erweitert. Der echte Proxy-/RecordingStore-Nachweis prüft den Store-Dispatch ohne äußeren Audit-Scope; er behauptet keine zusätzliche SQL-AuditStore-Probe.

Verarbeitungsinventar der Speicherverwaltung

Daten / Zweck Speicherort und Empfänger Aufbewahrung / Grenze
Zentrale Dauer, Aktivierungsstand, erste Aktivierung und Revision; unveränderlicher Änderungsnachweis mit Actor-ID, Operations-ID und UTC-Zeit Eigene Filing-Policy-/Änderungstabellen in der App-Datenbank; aktuell geeignete Produktadministration mit getrenntem Policyrecht Ausgangsdauer 90 Tage, technisch 1–3650 Tage konfigurierbar; Automatik zunächst aus. Inhalts-Purge entfernt keine Policy-/Änderungshistorie. Eine Änderung der Dauer behauptet keine gesetzliche Frist.
Fristbasis, Schutzbeobachtung, Auftragsursache, gebuchter Termin und gegebenenfalls manuelles Vorziehen Jeweils Filing- beziehungsweise Hearings-eigene Zeile und Historie; Fachansichten prüfen weiterhin eigene/gemeinsame Bereiche und Verfahrensrechte Restore setzt die Fristgeneration zurück. Inhalt verschwindet erst nach tatsächlichem erlaubtem Purge; Referenz und eigener Operations-/Verlaufsnachweis bleiben erhalten. Keine zusätzlichen privaten Dateimetadatenkopien.
Prüflauf, Generation, Cursor, technische Referenz-/Inhaltskennungen, Revision, Länge und Befundart Eigene Filing-Kontext-/Run-/Item-/Findingtabellen; reine technische Administrationsansicht unter Storage.Read Historische Prüfläufe/Befunde werden nicht automatisch gelöscht; behobene Befunde werden beim passenden neuen Vollabgleich geschlossen. Keine Namen, Pfade, Eigentümer oder Inhaltsbytes. Existenz und Länge belegen keine Byteidentität.
Ausdrückliche Freigabeautorisierung mit Actor-ID und UTC-Zeit Am vollständigen Prüflauf; getrenntes Storage.Release und frische Produktadmin-Eignung Bleibt als Autorisierungsnachweis erhalten. Ein Versuch ist keine Behauptung erfolgreichen externen Release; dessen aktuelle Bindung wird separat geprüft.
Kontextkennung, neue Generation und exakt freigegebener Run/Revision Kanonische externe Generationsdatei, .activated und .lock im app-eigenen Betriebsverzeichnis; ausschließlich App- und befugte Betriebskonten Außerhalb von DB-/Inhalts-Restore erhalten. Fehlender oder beschädigter aktivierter Zustand sperrt statt Rückfall. Keine Nutzerprofile oder Dokumentinhalte.

Kapazitätsmessung und Bestandsinventar sprechen ausschließlich den belegten App-SQL-/ Dateisystemspeicher an. Neue Seiten verwenden vorhandene In-Process-Dienste; es entstehen keine Drittanfragen, Cookies, localStorage- oder sessionStorage-Einträge. Fehleransichten geben keine Providertexte weiter. Der technische Befund ersetzt weder den von RAL zu formulierenden Aufbewahrungshinweis noch dessen tatsächliche rechtliche Freigabe; dieser externe Nachweis bleibt in HP65 unter DOD-PRIV-04 ausdrücklich offen.

Verarbeitungsinventar für Zustellmeldungen und Rundnachrichten

Technischer Abgleich vom 19.10.2026 für HP76/77. Die Tabelle beschreibt den Codebestand, keine rechtliche Freigabe und keine Frist. Bereits entschieden sind RAL als Verantwortlicher, Offlimits für Hosting, Keycloak und Mailversand sowie der unterzeichnete AV-Vertrag (HP67 O2/O6/O16). Offen sind Aufbewahrung und Hinweise sowie tatsächliche Betriebszugriffe, Speicher-/Backuporte und der in O6 nicht ausdrücklich benannte Betreiber des Rückläuferpostfachs. Bis dahin sind PRIV-01/03/04 nicht abgeschlossen; die gemeinsame Zuarbeit steht in HP67 §8.3.

Entität Eigene gespeicherte Felder / Datenkategorien Zweck Speicher Zugriff / Empfänger Aufbewahrung
HearingDeliveryReport ExternalReference, ReceivedAtUtc, Sender, Subject, Reason, StatusCode, OutboxMessageId, ResolvedAtUtc, ResolvedByUserId, ResolutionNote: Rückläuferreferenz/Eingang, Absender/Betreff, klassifizierter Grund/Status, mögliche Versandzuordnung und interner Klärungsnachweis Nicht sicher zuordenbare Meldungen klären; Referenz verhindert doppelte Verarbeitung RALQMHearingDeliveryReports in der App-SQL-Datenbank Interne Konten mit RALQM.Hearings.DeliveryReports.Review; Referent/Systemadministrator laut Rollenkomposition; keine Portalansicht Erledigen setzt Bearbeiter/Zeit/Notiz und löscht nichts. RAL entscheidet Frist oder begründete Aufbewahrung einschließlich Dublettenschutz.
HearingOutboxBounce OutboxMessageId, AttemptNo, ExternalReference, ReasonCode, ReceivedAtUtc: Meldungsreferenz, Versuch, klassifizierter Grund und Zeitpunkt; kein Meldungstext oder Adressfeld Empfangene Unzustellbarkeit genau einem Versandversuch zuordnen, verspätete und wiederholte Meldungen unterscheiden RALQMHearingOutboxBounces in der App-SQL-Datenbank Versandstatus über die jeweils berechtigten Hearings-/Rundnachrichtendienste, Verarbeitung durch den Hintergrunddienst Append-only-Verlauf; keine automatische Löschung. RAL entscheidet mit dem Outbox-/Verfahrensnachweis.
HearingCircular InstitutionGroupId, GroupName, CultureName, Subject, Body, TextsJson, SourceTemplateId, SourceTemplateRevision, Status, SentAtUtc, SentByUserId, RecipientCount, AttachmentsJson, SkippedJson: Gruppe, Textfassung der Standardsprache und Texte weiterer Sprachen (TextsJson, RAL-QM-HP-101)/Vorlagenherkunft, Sendemarkierung/Akteur/Zahl, Dokument-IDs/Dateinamen und übersprungene Institutionen/Gründe Verfahrensfreie Rundnachricht vorbereiten und einmalig mit eingefrorenem Stand versenden RALQMHearingCirculars in der App-SQL-Datenbank Interne Konten mit RALQM.Hearings.Circulars (Referent/Systemadministrator); Nachrichtentext und Dateien an ausgewählte gültige Standardkontakte/Vertretungen, jeweils als Einzelmail Verwerfen löscht Anhanginhalt transaktional und setzt Soft Delete; versandter Wortlaut unveränderlich. RAL entscheidet Fristen für Soft-Delete-Reste, Serien und Nachweise.

Alle drei Aggregate besitzen zusätzlich die ABP-ID, Concurrency-/ExtraProperties und Erstellungsmetadaten (Zeit und optionale Actor-ID). Rundnachrichten besitzen außerdem Änderungs- und Soft-Delete-Metadaten. Diese technischen Angaben bleiben in derselben Datenbank und haben dieselbe Zugriffs-/Aufbewahrungsgrenze; sie sind keine separaten Empfänger.

  • Aktionsaudit: AbpAuditLogs/AbpAuditLogActions speichern zusätzlich Benutzer, Zeitpunkt, Dienst/Methode und technische Eingaben wie Meldungs-ID und gelesenen Stempel. Der Zugriff folgt AuditLogging.AuditLogs, nicht dem Klärungsrecht. Der Klärungsvermerk bleibt ausschließlich an HearingDeliveryReport.ResolutionNote: Seit der Korrektur vom 19.10.2026 schließt [DisableAuditing] die Eingabe Note aus den Aktionsparametern aus. Eine echte ABP-/SQL-Probe prüft die weiterhin gespeicherte Fachnotiz und Auditaktion ohne Freitextkopie. Vor der Korrektur konnten Notizen zusätzlich in Parameters stehen; bestehende Auditdaten werden nicht verändert. RAL muss deren Aufbewahrung mitentscheiden.
  • Betriebslogs des Rückläufereingangs: Nur interne Meldungs-/Outbox-IDs, Versuchsnummer, IMAP-UID, klassifizierter Grund/Ergebnis und Exception-Typ werden protokolliert. Externe Message-IDs, Meldungsfelder sowie Exception- und InnerException-Inhalte bleiben ausgeschlossen, auch bei Abruf- und Disconnectfehlern. Vor dieser Nachprüfung konnten externe Message-IDs und rohe Providerfehler in Logs stehen; bestehende Logs/Backups werden nicht bereinigt und gehören in die noch zu bestätigende Betriebsaufbewahrung. Die Dublettenreferenz bleibt unverändert in den geschützten Fachdatensätzen, damit bestehende Meldungen nicht erneut wirken.
  • Postfach: ImapHearingDeliveryReportMailbox liest vollständige Rückläufer/Antworten temporär als Bytes (bis MaxReportBytes) und verschiebt sie erst nach dem DB-Commit von Folder nach ProcessedFolder. Dort bleiben vollständiger Meldungstext, Header und gegebenenfalls zitierte Originalinhalte beim Postfachbetreiber erhalten. Das Verschieben ist keine Löschung. Betreiber, weitere Zugriffe, Ort und Frist muss RAL-Betrieb bestätigen. Die App speichert aus der Meldung ausschließlich die oben genannten Klärungsfelder; der Parser übernimmt weder vollständigen Meldungstext noch die DSN-Empfängeradresse. StatusCode übernimmt nur den numerischen erweiterten Status nach RFC 3463 §2, ohne Providerkommentar; ungültige Werte bleiben leer beziehungsweise verwenden bei zugeordneten Bounces den vorhandenen neutralen Fehlercode. Absender und Betreff sind die ausdrücklich gewählten Klärungsfelder, keine aus dem Status abgeleiteten Personendaten.
  • Outbox und Betriebsnachweise: HearingOutboxMessage hält pro Einzelmail Empfängeradresse, Institutions-/Kontakt-/Vertretungsstand, Betreff/Körper (vorbereitet und tatsächlich gesendet), Kontext-ID, Versuch, Sendestatus und Bounce-Zuordnung fest. HearingSendAttempt, der Verfahrensverlauf und HearingIncident ergänzen technische IDs, Zeitpunkte, Versuch und Fehlerklassen; keine rohe Providerexception oder zusätzliche Meldungsinhalte im Log. Zugriff bleibt bei den jeweiligen Versand-/Outbox-/Störungsrechten. Offene Retries dürfen durch eine spätere Löschregel nicht ihren eingefrorenen Stand verlieren. RAL entscheidet Aufbewahrung gemeinsam mit den obigen Daten; derzeit kein automatischer Ablauf.
  • Anhänge: Originalbytes und Descriptor-Metadaten liegen über den CAF-Port in ABP File Management auf dem transaktionalen Datenbank-BLOB-Provider. Die Rundnachricht speichert nur IDs und Dateinamen. Upload prüft den Scan, Versand verlangt Clean. Entwurf verwerfen bzw. Anhang entfernen entfernt die zugehörigen Bytes transaktional; für bereits versandte Serien fehlt die von RAL bestätigte Frist unter Erhalt bestehender Referenzen.
  • Transport/Empfänger: SMTP erhält pro Nachricht genau Haupt- oder Vertretungsempfänger, Text und Anhänge; keine gemeinsame CC-/BCC-Liste. Ein Retry benutzt denselben eingefrorenen Wortlaut und dieselbe Adresse. SMTP und IMAP sind serverseitige Verbindungen zum konfigurierten Betreiber, keine Browser-Drittanfragen. Für den Mailversand gilt Offlimits (O6); konkrete RAL-Transportkonfiguration und Betreiber des dedizierten IMAP-Postfachs sind noch nachzuweisen, ohne die bereits bestätigte AV-Grundlage erneut zur Entscheidung zu stellen.
  • Browser-Speicher: Beide Seiten verwenden bestehende authentifizierte Interactive-Server- Aufrufe und legen keine neuen Cookies, localStorage- oder sessionStorage-Einträge an. Das Cookie-Inventar bleibt HP67 §3. Der technische Origin-Nachweis steht in den HP76/77-DoD-Einzelbewertungen.

MessagingPrivacyInventoryTests gleicht die gesetzten Entitätsfelder und die drei Tabellenspeicher mit dieser Tabelle ab und hält die separaten Postfach-/Outbox-/Anhang- Speicher sichtbar. Die Registrierung dieses Kapitels als HP-gateInputs stellt sicher, dass eine Änderung am Inventar den prüfenden App-Gate auswählt. Der Test ersetzt nicht die RAL-Entscheidungen zu Aufbewahrung und Betroffenenhinweisen.

3. Persistierte Kennungen

Zwei getrennte Präfixräume, konsequent nach Ownership:

Präfix Geltungsbereich Beispiele
RALQM anwendungsspezifisch (Gütezeichen-Anhörungsportal) Tabellen RALQMHearings, RALQMInstitutions; Permission-Gruppe RALQM; Fehlercode-Namespace RALQM.<Feature>:…; Rollen RALQMAdministrator, RALQMCaseOfficer, RALQMManagement; Lokalisierungsressourcen RALQMHearings, RALQMInstitutions
CAF wiederverwendbare, anwendungsneutrale CAF-Module Tabelle CAFDocumentShareGrants (CAF.Modules.Documents)

Technische Slugs (URLs, Docker-/Config-Bezeichner) bleiben bewusst kleingeschrieben, zum Beispiel ral-qualitymarks, und folgen nicht dem Groß-Präfix der persistierten Kennungen. Projekt- und Namespace-Namen (CAF.Applications.RAL.QualityMarks.HearingPortal.*) sind von dieser Konvention unabhängig und ändern sich durch die Umbenennung nicht.

Diese Trennung ist mehr als Namenskosmetik: Sie macht Ownership beim Lesen einer Tabelle, eines Fehlercodes oder einer Permission sofort erkennbar, ohne den Quellcode zu öffnen, und verhindert, dass ein wiederverwendbares CAF-Modul unbemerkt anwendungsspezifisches Wissen aufnimmt (siehe Bausteinsicht zur Modulgrenze CAF.* → HearingPortal.* verboten).

4. Lokalisierung

  • UI-Texte sind Deutsch (de-DE als führende Kultur), Code-Identifier und Kommentare Englisch, Logs strukturiert/Englisch.
  • Sprachen der Anwendung und Standardsprache stammen aus der Sprachverwaltung (ILanguageProvider, Einstellung Abp.Localization.DefaultLanguage); Hearings löst sie zentral über HearingLanguages auf und rechnet Kulturnamen nicht um (RAL-QM-HP-101, siehe Kommunikationssprache je Empfänger).
  • Jedes Feature-Contracts-Projekt besitzt seine eigene Lokalisierungsressource und DE/EN-JSON, zum Beispiel HearingPortal.Authorization.Contracts/Localization/Authorization/{de-DE,en}.json. DE- und EN-Schlüssel müssen in beide Richtungen übereinstimmen; hart codierte UI- oder Business-Exception-Texte sind verboten.
  • Fehlercode-Namespaces folgen demselben RALQM.<Feature>:-Schema wie die Fehlercodes selbst und werden per AbpExceptionLocalizationOptions.MapCodeNamespace an die Ressource des jeweiligen Feature-Contracts-Projekts gebunden (siehe HearingPortalAuthorizationContractsModule.ConfigureServices: MapCodeNamespace("RALQM.Authorization", typeof(HearingPortalAuthorizationResource))). Ein Fehlercode wie RALQM.Authorization:StaffAccounts:ConcurrencyConflict löst damit automatisch den passenden lokalisierten Text auf.
  • ABP Language Management liefert den datenbankgestützten Sprach-/Textprovider im Betrieb. Einen Cookie-Hinweis gibt es nicht (O8 der Datenschutz-Spec, entschieden 2026-10-01): ABPs Cookie-Einwilligung wird nicht registriert (AbpCookieConsentOptions.IsEnabled bleibt falsch), keines der beiden Layouts bindet die Hinweiskomponente des UI-Kits ein, und ralqm-consent wird nicht gesetzt. Belegt durch ShellCompositionTests.No_layout_mounts_the_cookie_notice_and_no_runtime_switch_exists und ShellCompositionTests.Shell_registers_each_owned_option_and_menu_contributor_once.
  • Ausnahme mit klarer Grenze — Wertelisten: Die Anzeigenamen der vier Wertelisten sind keine Ressourcentexte, sondern Spalten am Datensatz: NameDe ist Pflicht, NameEn optional, und fehlt das Englische, zeigt auch die englische Oberfläche den deutschen Namen. Grund ist die Herkunft der Texte: Ein Ressourcenschlüssel gehört einem Entwickler, der ihn ausliefert; diese Namen legt ein Administrator zur Laufzeit an und ändert sie, ohne dass jemand etwas ausliefert. Über Language Management wären sie an einem zweiten Ort gelandet, an dem eine neue Ausprägung namenlos bliebe — deshalb dort nicht geführt (Begründung: ADR-07). Unverändert lokalisiert bleibt alles um die Werte herum: Beschriftungen und Hinweise der Pflegeseite, die Namen der Listen selbst und die Fehlercodes unter RALQM.ValueLists:. Die früheren Schlüssel Hearings:MarkType:* und Hearings:ProcedureType:* sind entfallen.

5. UI- und Cohesion-Grenzen

  • Eigene CAF-/GZ-Oberflächen rendern ausschließlich Cohesion (Syncfusion); MudBlazor, Syncfusion-Rohkomponenten oder Blazorise dürfen von Fachcode nicht direkt verwendet werden. Ausgenommen sind allein die MVC-Razor-Pages unter /Account/** auf dem LeptonX-MVC-Theme. Blazor-seitig gibt es keine Ausnahme mehr; die ABP-Administrationsansichten sind Produktseiten, und weder ein MudBlazor- noch ein Blazor-LeptonX-Paket ist noch referenziert. Blazorise gelangt transitiv ueber ABPs Blazor-Theming-Paket in den Graphen - gerendert wird davon nichts, und seine Stylesheets werden aus dem globalen Bundle entfernt.
  • CAF.Modules.UI.Blazor registriert Cohesion, Lizenzprüfung, Assets/Bundles und die Interactive-Server-Policy zentral; es besitzt selbst keine Produktroute.
  • Feature-Komponenten setzen kein @rendermode und bleiben render-mode-neutral; der Host wählt Interactive Server. JavaScript-Interop läuft prerender-sicher erst nach interaktivem Rendern.
  • Jede datengebundene Oberfläche behandelt, soweit fachlich relevant: Loading, Empty, Error/Retry, Forbidden, Validation, Saving und Stale Concurrency mit Neu-Laden-Option.
  • Eine Suche oder ein Neuladen, das die Seite anfordert, während das erste Laden eines Grids noch läuft, holt Cohesion seit 3.6.3-alpha.0.17 nach dem Created-Ereignis einmal nach (UP-57). Die Grid-Adaptoren führen dafür keinen eigenen Lesestand mehr; die Produktbrücke CAFGridDataAdaptor.CaptureReadState ist am 2026-10-10 entfernt. Gespeicherte Standardansichten fordern nach ihrem Eintreffen weiter selbst ein Laden an. Regel, Grenzen und bUnit-Nachweise: Grid-Muster §15.
  • WCAG 2.1 AA ist Mindestziel: semantisches HTML, sichtbarer Tastaturfokus, zugeordnete Labels und nicht ausschließlich farbliche Statusanzeige.

Rechtsnavigation und Browser-Speicher (RAL-QM-HP-67)

Der Keycloak-Client des HearingPortals waehlt das app-eigene Login-Theme ralqm-legal. Es erbt ueber das kundenweite Basistheme ral-v1 die Formulare von keycloak.v2 (RAL-QM-HP-102, ADR-23). Die bestehenden Label-/Control-Korrekturen in template.ftl und login-config-totp.ftl sowie der native footer.ftl-Hook mit drei lokalisierten Rechtslinks bleiben app-eigen erhalten. Die absolute Client-BaseUrl bestimmt die Ziele; Anfrageparameter werden nicht als Rechtsziel verwendet. referrerpolicy="no-referrer" verhindert die Weitergabe der Anmelde-URL beim Oeffnen. Der Realm-Standard ral-v1 deckt clientlose Ablaufseiten ab; ohne Clientkontext erscheinen keine geratenen HP-Rechtsziele. Andere Realms bleiben unveraendert. Lokaler und Kunden-Compose mounten beide Themeverzeichnisse lesend; die echte Keycloak-Testfixture kopiert beide in ihren eigenen Container. Der CI-Keycloak-Service liest beide Themes desselben Checkouts ueber den nativen Folder-Provider. Diese Auslieferungsdefinition ist keine Freigabe oder Durchfuehrung eines Kunden-Deployments. Ein clientbezogener Login-Footer ersetzt kein realmweit ausgewaehltes E-Mail-Theme.

Die Blazor-Shell behält Rechtslinks in Leiste und Kontomenü. Der Host ergänzt im MVC-Accountlayout dieselbe lokalisierte Rechtsnavigation und Betreiberangabe über AbpLayoutHookOptions, LayoutHooks.Body.Last, ausschließlich StandardLayouts.Account. Er verwendet die vorhandene kompilierte MVC-Partial; die Theme-Layouts werden nicht kopiert. Der MVC-Skriptbundle setzt nach abp.js und vor der Anwendungskonfiguration abp.clock.trySetBrowserTimeZoneToCookie = false: das nicht ausgewertete Zeitzonencookie wird nicht mehr neu angelegt. Die Cookie-Seite und ihre Inventarprüfung bilden dies ab. Die GDPR-Selbstbedienung beschreibt ausdrücklich nur den lokalen Kontoumfang; eine vollständige Fachdaten-/Keycloak-Auskunft oder -Löschung wird nicht zugesagt.

Erklärung zur Barrierefreiheit (RAL-QM-HP-79)

Die Shell besitzt /Barrierefreiheit und ihr eingebettetes Register src/Shell/Legal/known-barriers.json. Die Erklärung verwendet denselben Betreiber wie das Impressum und die adaptive Rechtsseitenkomposition für anonyme Besucher, interne Konten und Institutionskonten. Portal-, Anmelde-, Abmelde- und Fehlerlayouts verlinken dieselbe Route. Semantische Dokumentstruktur bleibt HTML; Bedienelemente folgen Cohesion.

Accessibility:FeedbackEmail, StatementDate und ConformityStatus sind typisierte, beim Start geprüfte Konfiguration. Fehlende Pflichtangaben verhindern den Start. Die am 15.10.2026 im Benutzerauftrag ausgewählte Adresse barrierefreiheit@ral.de steht in der Basisdatei und gilt auch in Development. Einrichtung, Empfang und Betreuung bleiben als HP79-DEV-04 bis zur betrieblichen Bestätigung offen. Ein Status vollständiger Konformität ist bei noch vorhandenen Registereinträgen unzulässig.

Jede bekannte Einschränkung hat eine stabile ID, betroffene Wege, Erfassungsdatum, lokalisierte Beschreibung/Begründung und einen Zeitplan. Die bewusste Entwicklungsabnahme vom 15.10.2026 lässt diese Einschränkungen offen sichtbar. Sie ist keine Produktionsfreigabe. BrowserGate prüft die tatsächlich zusammengesetzten kundenseitigen Seiten gegen alle axe-WCAG-A/AA-Befunde sowie mit Tastatur-/Fokuspfaden. Die technische Prüfausnahmeliste tests/BrowserGate/a11y-ausnahmen.json bleibt von diesem öffentlichen Register getrennt; ein Registereintrag macht einen fehlenden Prüflauf nicht erfolgreich.

Bestätigungsdialoge

Zweistufige Entscheidungen folgen einem von zwei Mustern. Festlegung vom 18.09.2026 (Anlass: Review von RAL-QM-HP-5): Bis dahin nannte dieser Abschnitt nur das erste Muster, obwohl die Verfahrensakte ihre Rückfragen seit dem 26.08.2026 nach dem zweiten baut. Das Unterscheidungsmerkmal – gesperrte oder bedienbare Seite – ist damit eine Entscheidung für künftige Oberflächen, keine nachträgliche Beschreibung des Bestands.

Muster 1 – Kit-Bestätigungsdialog, wenn die Seite gesperrt werden soll. CohesionConfirmDialog-Instanzen im owning .Blazor-Projekt (Stand 18.09.2026: 13 Seiten in Institutions, Filing, Identity, Gdpr, TextTemplates und Languages – etwa das Löschen einer Institution und seit RAL-QM-HP-10 die Dublettenwarnung vor dem Speichern und die Deaktivierungswarnung): Titel, Text und Schaltflächen aus der Feature-Ressource, Visible einseitig gebunden, die Antwort räumt den auslösenden Zustand als Erstes. Eine Warnung sperrt nicht — „Trotzdem speichern“ bleibt möglich und ist bewusst nicht die Primäraktion (SPEC-RAL-QM-HP-10 A1). Nebengrund für diese Komponente: Der Dialog rendert unter bUnit; der generische CohesionDialog tut das nicht (siehe LanguageTextsPage). Fokus in den Dialog ist Komponentenfunktion des Kits. Die Rückkehr zum Auslöser leisten drei Institutions-Seiten noch selbst über ElementFocus in HearingPortal.UI.Blazor (nach Abbrechen und nach der bestätigten Deaktivierung in der Liste): ein Behelf mit Eintrag im Override-Register des Repositorys (docs/arc42/02-bausteine.md, nicht im arc42-Ordner dieser Anwendung), gemessen an 3.6.2-alpha.0.13. Laut Kit-Quelle ist die Ursache seit 3.6.3-alpha.0.6 behoben; der Browsernachweis für den Rückbau steht aus (cohesion-upstream.md, UP-23).

Muster 2 – Bereich in der Seite, wenn sie bedienbar bleiben soll. Die Verfahrensakte (HearingDetailPage) stellt ihre sechs Rückfragen – Abschließen, Stornieren, Abbrechen, Teilnahme zurückziehen, Unterlage entfernen (RAL-QM-HP-21) und seit RAL-QM-HP-5 Zugang entziehen oder wieder freigeben – als Bereich an der Stelle der Aktion. Außerhalb der Verfahrensakte fragt nur das eigene Konto in der Seite zurück (OwnAccountPanel, Signatur entfernen, RAL-QM-HP-89) – dort als Schaltflächenwechsel an Ort und Stelle, ohne eigenen Bereich. Der Bereich ist role="group" mit Titel (aria-labelledby), bei Bedarf Frage oder Hinweis (aria-describedby; seit RAL-QM-HP-5 die Frage des Zugangsentzugs und der Hinweis beim Ausnehmen eines Pflichtpartners), Cohesion-Eingaben und -Schaltflächen, ohne Backdrop und ohne Fokusfalle: semantisches HTML mit Cohesion-Komponenten (Zone 1 der Cohesion-Grenze), keine zweite Dialog-Fassade – und bewusst kein Dialog im ARIA-Sinn, die übrige Seite bleibt lesbar und bedienbar. Vier der sechs Bereiche tragen ein Eingabefeld (Begründung oder Vermerk) und sind damit kleine Formulare; „Unterlage entfernen“ und der Zugangsentzug sind reine Ja/Nein-Rückfragen. Den Fokus führt seit RAL-QM-HP-94 (11.10.2026) jeder dieser Bereiche so wie zuerst der Zugangsentzug: beim Öffnen auf den Bereich, beim Abbrechen zurück zum Auslöser, nach dem Erfolg auf die Gegenaktion oder, wo keine bleibt, auf eine Ergebniszeile (#hearing-lifecycle-result, #hearing-document-result); ein Fehlerfokus geht vor (Zugangsentzug im BrowserGate gemessen, Stufe parallel-sessions; die übrigen Bereiche in HearingDetailConfirmationFocusTests). Führt ein solcher Bereich den Fokus, dann leistet das die Seite selbst per id über ElementFocus, weil CohesionButton und CohesionIconButton keine ElementReference bieten. Das ist reguläre Seitenfunktion und kein Behelf: Es gibt keine Kit-Fokusfalle, die die Rückgabe übernehmen könnte. So nutzen den Helfer InstitutionGroupAssignPage für ihren Korb-Umschalter (RAL-QM-HP-3), HearingDetailPage für den Zugangsentzug (RAL-QM-HP-5) und die übrigen Rückfragen (RAL-QM-HP-94), die Kontaktmeldungs-Panels im Portal und in der Verfahrensakte (RAL-QM-HP-93, -94), InstitutionBindingPanel, OrganisationLoginPanel, ConsentDocumentsPage und InstitutionsPage beim CSV-Import (RAL-QM-HP-95) sowie OwnAccountPanel für die Meldung eines gescheiterten Speicherns (RAL-QM-HP-99).

Die Deaktivierungswarnung (SPEC-RAL-QM-HP-10 AC-10) unterscheidet sich in der Quelle der Frage: Nicht die Oberfläche fragt vorab, sondern der Anwendungsdienst weist den Wechsel aktiv → inaktiv ohne Bestätigungskennzeichen ab (RALQM.Institutions:DeactivationRequiresConfirmation, Zahl der laufenden Verfahren als Exception-Daten), auf beiden Pfaden SetActiveAsync und UpdateAsync und vor der ersten Mutation. Die Seite erkennt die Abweisung am Fehlercode, stellt die Frage mit der genannten Zahl und wiederholt den Aufruf mit gesetztem Kennzeichen. So gilt die Regel auch für jeden Aufrufer ohne Oberfläche; die Zahl stammt vom abgewiesenen Schreibversuch, der bestätigte Aufruf zählt nicht erneut.

Die Kontaktfrage (RAL-QM-HP-9 AC-5, A7) folgt demselben Muster mit zwei Unterschieden. Der Dienst stellt sie als letzte Prüfung vor dem ersten Speicherschritt (E14), und die Abweisung (RALQM.Institutions:ContactDeactivationRequiresConfirmation) nennt neben der Zahl jeden betroffenen Kontakt mit seinen Verfahren. Die Maske zeigt genau diesen Stand, nicht den beim Öffnen gelesenen Nutzungshinweis. „Trotzdem deaktivieren“ ist nicht die Primäraktion. Die Bestätigung setzt das Kennzeichen nur an den genannten Kontaktzeilen; Abbrechen speichert nichts und schaltet die genannten Kontakte in der Maske wieder ein. Deaktiviert ein Speichern Institution und Empfänger zugleich, kommt die Institutionsfrage zuerst, und ihre Antwort reist mit der Kontaktantwort. InstitutionUiErrorClassifier liest die Abweisung in-process als Vertragsliste und über den Remote-Proxy als JSON; unbrauchbare Daten ergeben keine Frage. Weitere Kontaktregeln (Standard, Vertretung, Pflichtpartner-Kennzeichen) nennt die Maske mit ihrem eigenen Text statt des allgemeinen Speicherfehlers. Den Nutzungshinweis liest die Maske beim Öffnen einmal über GetContactRunningHearingsAsync; scheitert das, bleibt die Maske benutzbar und zeigt einen eigenen Hinweis mit Wiederholen.

Dateiauswahl im Portal: ein Feld je Auswahl (RAL-QM-HP-38)

Die Teilnahmeansicht (RAL-QM-HP-38 AC-8) lässt Anlagen vor dem Absenden hinzufügen und wieder entfernen; gesendet werden sie erst mit der Stellungnahme, in deren einem Aufruf. Mehrere Auswahlen nacheinander addieren sich. Ein Dateifeld (InputFile) hält nur die Dateien seiner letzten Auswahl lesbar; deshalb bekommt jede Auswahl, die Dateien geliefert hat, ein eigenes Feld: Das benutzte bleibt eingebaut, aber hidden – aus der Anzeige, aus der Tab-Reihenfolge und aus dem Accessibility-Baum –, ein frisches Feld übernimmt Kennung (participation-attachment-upload), Beschriftung und Hinweis; nach dem Speichern verschwinden die benutzten Felder. Bei der Auswahl weist die Seite ab, was die Konfiguration abweist – Dateityp nach den erlaubten Endungen aus der Übersicht, Größe je Datei, Anzahl über die verbleibenden Plätze –, ohne dass ein Byte gesendet wird. Beim Senden ist die konfigurierte Größe die Obergrenze jedes Browser-Lesevorgangs, und der Server prüft Typ, Inhalt und Größe erneut. Nach jeder Abweisung des Servers bleiben Eingaben und gewählte Dateien im Formular; nur die neutrale Abweisung lädt neu, Stempelkonflikt und ResponseAlreadySubmitted bieten das Neuladen als eigene Aktion an. Nachweise: ParticipationPortalUiTests (Several_selections_add_up_and_each_one_keeps_its_own_input_until_the_send, A_file_of_a_type_the_policy_does_not_accept_is_refused_when_chosen, A_file_over_the_size_and_a_selection_over_the_count_are_refused_when_chosen).

6. Fehlerbehandlung

Produktfehlerseiten (RAL-QM-HP-80)

Der Host beantwortet leere 403/404/500 von Produktrouten direkt mit einem statischen ErrorDocument und der gemeinsamen ShellErrorView. Der HTTP-Status bleibt erhalten, Cache-Control: no-store, private verhindert Zwischenspeicherung; es gibt keinen Redirect und keinen erneuten Circuit der fehlgeschlagenen Seite. Unbehandelte Ausnahmen vor Beginn der Antwort verwenden außerhalb von Development dasselbe Dokument mit HTTP 500 und dem serverseitigen ASP.NET-Request-Identifier. Development behält die Entwickler-Fehlerseite.

Der Fehlerpfad liest ausgelieferte DE/EN-Ressourcen und rendert vorhandene Cohesion-/Portal- Templates ohne datenbankgestützte Menüs, Profil- oder Language-Management-Abfragen. Die Institutionsrolle wählt nur das datenfreie Portal-Chrom, nie einen Fachzugriff. /api, /health, /swagger und /_blazor werden nicht in HTML umgewandelt; MVC-Kontoseiten behalten ihren separaten ABP-Handler. Der anonyme Darstellungsendpunkt /fehler/{statusCode} enthält ausschließlich die drei Fehlervorlagen, keine Exception-Auslösung oder Fachdaten.

Im Router verwenden NotFoundPage, Zugriff-abgelehnt-Seite und NotAuthorized dieselbe Vorlage. Der Fokus liegt auf ihrer einzigen h1, der Rückweg ist ein gewöhnlicher Anker auf /. Lokalisierte ConnectionStatus-Elemente und circuitunabhängiges Reload begleiten die Framework-Reconnect-Klassen. Entscheidungen: ADR-13; Kriterien und Nachweise: SPEC-RAL-QM-HP-80.

Fachliche Fehlerverträge

  • User-facing Fehler verwenden stabile, im owning Feature definierte Error Codes nach dem Schema RALQM.<Feature>:<Bereich>:<Grund> (z. B. RALQM.Authorization:Provisioning:AmbiguousEmail, RALQM.Authorization:StaffAccounts:InvalidRole) statt Klartext oder generischer Exceptions.
  • Jeder Fehlercode ist über den Code-Namespace des Feature-Contracts-Projekts lokalisiert (siehe Abschnitt 4) — ein neuer Fehlercode ohne DE/EN-Paritätstext gilt als unvollständig.
  • Platzhalter im Text eines Fehlercodes füllt die Abweisung über ihre Exception-Daten, und zwar nach Schlüssel: {Schlüssel} wird durch Data[Schlüssel] ersetzt, WithData("0", …) füllt {0}. Nach dieser Regel formuliert ABP die Abweisung für die HTTP-API (DefaultExceptionToErrorInfoConverter). Dieselbe Regel wendet HearingUiErrorClassifier.DescribeCommunicationError auf jeden Hearings-Code an, den es nicht gesondert beschreibt – und gilt damit nur auf den Hearings-Seiten, die ihre Abweisungen darüber beschreiben: Bescheidung, Verfahrensakte samt Erinnerungsauswahl (HearingReminderBatchPanel), Einreichung, Versand, Kommunikationsvorlagen und der Kommunikationsschritt (HearingCommunicationStepPanel). Protokollrecherche und Teilnahme im Portal formulieren ihre Platzhaltercodes selbst. Die übrigen Oberflächen, die den bloßen Ressourcentext eines Codes zeigen (Freigabe und ihre Rücknahme, Postausgang, Antworten, Empfängerstand, individuelle Frist, Anlage, Kopie, Vorlagenbindung), rufen keine Operation auf, die einen Platzhaltercode wirft. Der bloße Text ist kein Ersatz, er enthält die Platzhalter unformatiert (RAL-QM-HP-46: „Bescheide versenden“ zeigte „({0})“, die Erinnerungsauswahl „{0}“ statt der Institutionen). Jeder Platzhaltertext nennt deshalb genau die Schlüssel, die jede seiner Wurfstellen anhängt – in beiden Sprachen gleich, bevorzugt benannt ({institutions}, {count}); ein Positionsplatzhalter {0} nur zu WithData("0", …). Was nicht jede Wurfstelle anhängt, gehört nicht in den Text des Codes: UnresolvedCommunicationPlaceholder nennt nur {placeholder}, denn Vorlagen-, Vorschau- und Wortlautprüfung hängen nur ihn an. Empfängerkennung, Zweck und Kanal ergänzt erst die Aussendung (HearingOutboxRenderer); die HTTP-API liefert sie in den Fehlerdaten, die Seiten nennen Empfänger und Kanal in einem eigenen Satz (Templates:UnresolvedPlaceholder, RAL-QM-HP-26 AC-6). Ein solcher eigener Zweig (HearingUiErrorClassifier.CodesWordedByOwnBranch) ergänzt die Schlüsselregel für die Seite, er ersetzt sie nicht. HearingUiErrorClassifierTests hält für jeden Platzhaltertext der Hearings die Schlüssel seiner Wurfstellen fest und wird rot, wenn der Text andere nennt (die Schlüssel selbst sichern die Tests der Wurfstellen); HearingExceptionLocalizationTests prüft jeden Platzhaltertext mit ABPs echtem Konverter. Die Abweisung der Wertelisten-Prüfung (RALQM.ValueLists:EntryInactive, :EntryNotFound) ist kein Hearings-Code, und die Hearings-Ressource hat für sie keinen Text: Verfahrensakte und Anlage formulieren sie über HearingUiErrorClassifier.DescribeValueListRefusal aus der Wertelisten-Ressource nach derselben Schlüsselregel (RAL-QM-HP-4 AC-2, HearingUiErrorClassifierValueListTests).
  • Geschützte Application Services unterscheiden deterministisch zwischen Validation, Not Found, Forbidden und Concurrency, soweit diese Unterscheidung keine Information an anonyme Nutzer leakt. Bei Ablehnung bleibt die Persistenz unverändert.
  • Für aufgegebene Gastdownloads und im Host gesperrte Direkt-Shares gilt bewusst das Gegenteil: unterschiedliche interne Ablehnungsgründe werden auf dieselbe äußere Antwort abgebildet (nicht enumerierbare Fehlerform, no-store, nosniff), damit IDs oder Tokens nicht erraten werden können.
  • Externe Calls (Keycloak-Admin-Port, IEmailSender) klassifizieren Fehler als Validation, Not Found, Authorization, Conflict oder Unavailable und verwenden Idempotency Keys statt stiller Wiederholung mit Nebenwirkung.

Kommunikationsvorlagen und Verfahrensstände (RAL-QM-HP-26)

RALQMHearingCommunicationTemplates speichert ausschließlich Name, Zweck, Kanal, Zustand, Revision und native Inhaltsreferenz. Die Texte liegen in ABP Text Template Management; TextTemplates.Runtime erlaubt neue unveränderliche Definitionen innerhalb der besitzenden Transaktion und liest dynamische Definitionen ohne einen veralteten Prozesscache. Statische ABP-Definitionen behalten Vorrang. Die generische ABP-Pflege prüft alle registrierten ITextTemplateWriteGuard vor Update und Restore. Die einzige Löschoperation des Ports (IManagedTemplateContentPort.DeleteAsync) gehört der einmaligen Kennungsumstellung und Kopienbereinigung des Migrators (RAL-QM-HP-101); eine verwaltete Vorlage ist sonst unveränderlich.

Draft → Active → Retired verwendet optimistische Concurrency. Ein gefilterter eindeutiger Index schützt Purpose/Channel bei Active; der Ersatz läuft unter Serializable. Ein SQL-Deadlock oder die Verletzung genau dieses Indexes wird als Concurrency-Konflikt zurückgegeben. Das eigene Pflegerecht erhalten Referent und Produktadministrator, die Geschäftsführung liest über Hearings.Default. Bestehende technische ABP-Grants ersetzen den Fachschreibpfad nicht.

Bei der Verfahrensanlage werden alle aktiven Texte in allen aktiven Sprachen der Sprachverwaltung samt Referenz und Revision in Hearing.CommunicationTemplatesJson festgeschrieben (seit RAL-QM-HP-101 sprachneutral: eine Katalogvorlage trägt je aktiver Sprache einen Text, der Text der Standardsprache ist Pflicht, Kennungen sind die Kulturnamen der Sprachverwaltung). Fehlt eine Sprachfassung, antwortet die Standardsprache; dieser Rückfall wird im Verfahrensstand gekennzeichnet eingefroren (ContentCultureName je Eintrag). Altverfahren können den angezeigten aktuellen Satz einmalig mit Actor, UTC-Zeit und gelesenem Verfahrensstempel übernehmen. Fehlende Zwecke sperren den jeweiligen Kommunikationsschritt. Der Seed läuft nur im Migrator. Er importiert die effektiven Altslots einmalig in einen leeren Katalog und ersetzt dabei die Literalsignatur „RAL Anhörungsportal“ durch {{Absender}}; bestehende Verfahrensstände bleiben unverändert. Bei gefülltem Katalog ersetzt er bei jedem Lauf aktive Nachrichtenvorlagen, deren Text noch die Literalsignatur trägt, über den HearingCommunicationTemplateManager durch einen aktiven Ersatz gleichen Namens mit ersetzter Signaturzeile (RAL-QM-HP-66, wiederholbar, Altvorlage stillgelegt, individuelle Texte erhalten, Audit-Scope für die Entitätsänderungshistorie); Vorlagen ohne Signaturzeile und ohne Platzhalter bleiben stehen, sie und Ersätze ohne {{Ansprechperson}} werden je Sprache als strukturierte Ereignisse gemeldet. Nach demselben Muster stellt er die beiden aktiven Erinnerungsvorlagen um, deren Text noch den ausgelieferten Wortlaut vor RAL-QM-HP-84 trägt (Betreff „Erinnerung, zweite Frist läuft“, Satz „Es läuft nun eine zweite Frist bis zum {{Frist}}.“; zweite Erinnerung mit dem Betreff „Statusänderung“ und dem Satz „läuft die zweite Frist für {{Institution}} am {{Frist}} ab.“): die eine Textregel HearingReminderWordingUpgrade ersetzt genau Betreff und Satz durch den ausgelieferten neutralen Wortlaut, alles Übrige bleibt zeichengenau; ein Text ohne den ausgelieferten Wortlaut bleibt stehen und wird je Sprache gemeldet, wenn er weiter von einer „zweiten Frist“ spricht. Dieselbe Regel gilt beim Import eines gespeicherten Altslots. Gebundene Textsätze laufender Verfahren bleiben unverändert; eine Laufzeitregel wie für die Signatur gibt es für den Wortlaut nicht (Entscheidung 08.10.2026).

Nachtrag RAL-QM-HP-51: ausdrückliche Ergänzungsbindung für nach RAL-QM-HP-26 eingeführte Zwecke. Die Regel „Fehlende Zwecke sperren den jeweiligen Kommunikationsschritt“ gilt unverändert. Der erste nach RAL-QM-HP-26 eingeführte Zweck, die Endfassungsbenachrichtigung (FinalVersionDelivery, 70), fehlt in jedem vorher gebundenen Verfahren; dafür gibt es einen ausdrücklichen Ergänzungsweg nach dem Altfallmuster, keinen stillen Rückfall auf den Katalog (SPEC-RAL-QM-HP-51):

  • Domäne. Hearing.SupplementCommunicationTemplates ist rein additiv: nur Zwecke ohne gebundenen Eintrag und nur aus der Allowlist der nach RAL-QM-HP-26 eingeführten Zwecke (Hearing.SupplementableTemplatePurposes, heute genau {70}). Verweigert wird in Completed/Cancelled und ohne vorhandene Bindung – dort bleibt die Altfall-Übernahme BindCurrentTemplatesAsync der Weg –, Fehlercode TemplateSupplementNotAllowed. Neue Einträge werden hinter das bestehende JSON gehängt; der gebundene Bestand bleibt byteidentisch.
  • Anwendungsdienst. IHearingCommunicationTemplateAppService.SupplementMissingTemplatesAsync folgt BindCurrentTemplatesAsync: Recht Update, Verfahrensstempel, erwartete Revision je aktiver Katalogvorlage. Actor und UTC-Zeit werden festgehalten; der Verlauf erhält CommunicationTemplatesSupplemented (830; Subjekt Verfahren, Detail Zweck und Revision, PII-frei).
  • Auslöser ist ausschließlich die Oberfläche. Abschluss-Panel und kombinierter Abschluss zeigen bei HearingDto.IsFinalVersionTemplateMissing (70 fehlt und mindestens ein Endfassungsempfänger mit wirksamem Endfassungs-Grant – dieselbe Empfängermenge wie die Vorprüfung im Abschluss) den Hinweis HearingFinalVersionTemplateNotice mit Revision und Betreff der aktiven Vorlage und der Aktion „Vorlage übernehmen“; der Abschluss-Button ist bis dahin mit Begründung gesperrt. Der Abschluss selbst ergänzt nie.
  • Vorprüfung im Abschluss. Vor jeder Nebenwirkung weist der Abschluss mit TemplateNotResolvable (purpose 70) ab, wenn mindestens ein Endfassungsempfänger mit wirksamem Endfassungs-Grant (Regel 7) existiert und 70 nicht gebunden ist (Laufzeitsicht, Schritt 6). Ohne Empfänger braucht der Abschluss keine 70. Die RAL-QM-HP-42-Festschreibung „fehlender Zweck → vollständiger Rollback“ bleibt unverändert.
  • Seed. Bei gefülltem Katalog importiert der Migrator zusätzlich die nach RAL-QM-HP-26 eingeführten Zwecke, für die keine einzige Katalogzeile besteht (heute nur 70; Ereignis 7071 HearingTemplateMissingPurposeImported), mit demselben Auflöser, derselben Signaturregel und demselben Audit-Pfad, wiederholbar ohne Wirkung. Eine Zeile des Zwecks in jedem Zustand – auch Entwurf oder stillgelegt – wird respektiert. Fehlende Alt-Zwecke importiert er nicht: Sie sind bewusste oder abweichende Katalogzustände (RAL-QM-HP-42-Festschreibung), keine still zu aktivierenden Lücken.
  • Verfahren von vor RAL-QM-HP-26 (CommunicationTemplatesJson = null) scheitern weiterhin am ersten fehlenden Zweck; ihr Weg bleibt BindCurrentTemplatesAsync.

HearingOutboxRenderer liefert Nachricht und optionalen Dokumenttext aus demselben Empfängermodell. Der Dokumentvertrag bereitet Text vor; Binärdatei, Anlage und Ablage werden in RAL-QM-HP-24 umgesetzt. Anredeform/Titel gehören Institutions und werden auf den Teilnehmer kopiert. Freie Bestandsanreden werden unverändert verwendet. Alle fünf Arten mit Frist behalten ihre bisherigen Datumsquellen; Abschluss und Abbruch führen keinen Datumsplatzhalter ein. In einem gebundenen Nachrichtentext wird die Literalsignatur beim Rendern durch {{Absender}} ersetzt — nie angefügt —, die Bindung selbst bleibt unverändert (RAL-QM-HP-66). Unbekannte, beschädigte oder nicht auflösbare Ausdrücke verhindern den vollständigen transaktionalen Enqueue. Der Worker prüft außerdem den gesamten noch offenen Batch vor dem ersten externen Send, einschließlich verzögerter Retries. Klartextlinks bleiben transient. Vorlagentexte, Teilnehmersnapshots und verschlüsselte Payloads werden nicht auditiert.

Empfängerstand je Zeile (RAL-QM-HP-6 AC-1). Der Renderer, die eine Stelle, die den Teilnehmer jeder Aussendung lädt und prüft, friert dessen operativen Empfängerstand in RenderedHearingMessage ein; HearingOutboxManager schreibt ihn vor dem Insert auf die Zeile (Recipient*-Spalten, siehe §2). Kein Sendeweg kann eine Teilnehmernachricht ohne Stand einstellen; eine spätere Änderung der Beteiligung oder der Stammdaten erreicht keine eingestellte Zeile.

Kommunikationsschritt und Begleitdokument (RAL-QM-HP-24). Der Renderer liest den Wortlaut aus dem Kommunikationsschritt des Zwecks – mit der Abweichung der Beteiligung, wenn sie eine hat –, legt den Schritt für automatische Auslöser vor der ersten Zeile aus der gebundenen Vorlage an (genau einmal, Systemeintrag) und setzt ReleasedAtUtc, sodass der Wortlaut ab der ersten Zeile eines Zwecks festgeschrieben ist. Nennt der Wortlaut weder {{Link}} noch eine Portaladresse, hängt HearingPortalLinkRule die Linkzeile einmal an; Link ist Platzhalter jedes Zwecks, und der Worker füllt ihn für jede Zeile, die ihn trägt, mit dem tokenfreien Teilnahmeziel aus RAL-QM-HP-31 – auch für die bisher credential-freien Arten. Je Teilnehmernachricht komponiert der Renderer den Brief (HearingCoverLetterContent: Briefkopf aus dem vollständigen Organisationsabsender (RequireAsync, sonst Abweisung vor Dokument und Zeile), Anschrift aus dem eingefrorenen Empfängerstand, Ansprechperson, Datum, Betreff, Wortlaut ohne die Mail-Signaturblöcke, Zeichnung – Einladung und Bescheid die freigebende Geschäftsführung, sonst der Zuständige, als Text ohne Bild); HearingOutboxManager schreibt ihn mit HearingCoverLetterPdfWriter (PDF 1.4, Helvetica WinAnsi, kein Paket) über IDocumentContentPort vor dem Insert und bindet die Datei an die Zeile; die Vertretungskopie trägt dieselbe Datei, ein Replay legt keine zweite an. Der Worker sendet eine Zeile mit Dokument als MailMessage mit Anhang, öffnet die Datei je Versuch über den Port (dieselben Bytes, nie ein fremdes Dokument) und hält bei MarkSent den versendeten Wortlaut fest. Die Endfassung der Oberfläche (IHearingCommunicationStepAppService.GetFinalPreviewAsync) komponiert denselben Brief über HearingOutboxRenderer.ComposeLetter ohne Nebenwirkung; ihr OutputStamp (Wortlaut, Abweichungen, Absenderblock, Zeichnungs-Id, gedruckter Zeichnungs- und Ansprechpersonenblock) ist der Fingerabdruck, den der Erstversand zurückverlangt (CommunicationOutputOutdated), wenn die Versandseite ihn mitgibt.

Ausnahme: Endfassungsbenachrichtigung ohne Begleitbrief (RAL-QM-HP-51, E4). Ob eine Teilnehmeraussendung einen Brief trägt, entscheidet genau eine Regel: HearingCoverLetterRule.Requires(kind) ist für jede Nachrichtenart wahr außer für die Endfassungsbenachrichtigung (FinalVersionDelivery, 70). Alle Stellen lesen dieselbe Regel: der Renderer komponiert für 70 keinen Brief und verlangt deshalb auch keinen vollständigen Briefkopf-Absender, HearingOutboxManager ruft kein CreateAsync auf und bindet kein CoverDocumentId, Endvorschau und OutputStamp führen weder Briefdatei noch Zeichnungsblock, und die Zusammenstellung einer Phase weist 70 als leer ab (CoverDocumentCompilationEmpty). Der Worker versendet eine Zeile ohne Dokument als einfache Nachricht. Die Endfassung selbst – die aktiven Abschlussanlagen – wird nie als Anhang versandt: Die Benachrichtigung verweist credential-frei ({{Portalhinweis}}, {{Link}}) auf den Portalabruf nach RAL-QM-HP-30/RAL-QM-HP-62, den die Endfassungs-Grants des Abschlusses öffnen. Nachweis: HearingOutboxCoverDocumentTests.The_manager_stores_a_cover_letter_exactly_for_the_kinds_that_require_one (Theorie über alle Arten). SPEC-RAL-QM-HP-24 AC-5 trägt den Nachtrag.

Vertretungskopie (RAL-QM-HP-9 AC-7, A10, A11). Hat der Kontakt eines Teilnehmers in dem Moment, in dem eine Aussendung in den Postausgang gelegt wird, eine wirksame Vertretung, entsteht eine eigene Postausgangszeile an die E-Mail-Adresse der Vertretung. Aufgelöst wird die Vertretung an genau einer Stelle, in HearingOutboxManager, und damit für jede Nachrichtenart: über den permissionfreien Port IInstitutionContactDeputyReader aus Institutions.Contracts, mit dem UTC-Zeitpunkt der ABP-Clock beim Einstellen; Zeitfenster und Aktivität prüft Institution.FindEffectiveDeputy. Die Kopie entsteht nur, wenn die Hauptzeile tatsächlich eingefügt wurde; eine Wiederholung mit bereits vorhandenem Schlüssel erzeugt keine nachträgliche Kopie. Sie liegt in derselben Unit of Work und in derselben Batch-ID wie die Hauptzeile und unterliegt ohne Ausnahme derselben Alles-oder-nichts-Regel der Aussendung. Sie ist unverändert: gleicher Teilnehmer, gleiche Nachrichtenart, gleicher gerenderter Text samt Modell und derselbe verschlüsselte Link-Payload und – seit RAL-QM-HP-6 – derselbe eingefrorene Empfängerstand, weil die Kopie denselben Empfänger vertritt, und – seit RAL-QM-HP-24 – dasselbe Begleitdokument; nur Kennung, Empfängeradresse und Idempotenzschlüssel unterscheiden sich. Der Schlüssel ist der vollständige Schlüssel der Hauptzeile mit dem Suffix :dep. Stimmt die Adresse der Vertretung ohne Groß- und Kleinschreibung und ohne Randleerzeichen mit der Empfängeradresse der Hauptzeile überein, entsteht keine Kopie (E12). Zustellstatus des Verfahrens und Postausgangsverwaltung zeigen die Kopie als zweite Zeile desselben Teilnehmers mit der Adresse der Vertretung, ohne eigene Beschriftung und ohne neue Spalte; im Zustellstatus folgt sie der Hauptzeile. Sendefehler der Kopie laufen je Zeile über die normalen Wiederholungen und Störungen des Postausgangs und berühren die Hauptzeile nicht. Seit RAL-QM-HP-31 trägt die Kopie einen tokenfreien Portal-Tiefenlink. Sie vermittelt kein Zugriffsrecht; die Vertretung benötigt ein eigenes aktives Institutionskonto. Wer RALQM.Institutions.Update hält, bestimmt weiterhin die Adressierung künftiger Kopien, aber keine Kontobindung. Die Vertretungszuweisung bleibt in der Entity History nachvollziehbar.

Accounts.ManageInstitutionBinding gehört Referent und Systemadministrator. Widerruf und Rücknahme sind getrennt von Kontosperre und Teilnahmeentzug, mit Akteur, UTC-Zeit, Concurrency und idempotenter Operation-ID. Die Kontoübersicht zeigt auch widerrufene Bindungen.

7. Teststrategie

Die Lösung trennt Nachweise nach Risiko statt einer einzigen Testebene zu vertrauen:

Ebene Prüft
Architekturtests Projekt-, Paket-, Modul- und Quellcodegrenzen (z. B. CAF.* → HearingPortal.* verboten)
Modul-/Application-Tests Invarianten, Permissions (positiv/negativ über den realen ABP-Proxy), Fehler, Idempotenz
SQL-Server-Integrationstests Mapping, Constraints, Concurrency, echte Rundreisen, from-empty
bUnit-Tests gerenderte Zustände, Fokusverhalten, Cohesion-Provider-Scan
BrowserGate (separat, exklusiv) realer Stack, Anmeldung, Hauptprozess, Permission-Denial, Assets, responsive Bedienbarkeit, axe/WCAG

Der zentrale Einstieg ist tools/scripts/Invoke-QualityGate.ps1. Er bestimmt betroffene Apps aus den vollständigen Ref-Diffs und den transitiven Projektreferenzen vor und nach der Änderung. Das app-eigene Gate führt Preflight, Restore, Build ohne Warnings/Errors, alle App-Solutiontests, dotnet ef migrations has-pending-model-changes und den BrowserGate aus. Fehlende oder übersprungene Pflichtnachweise machen den Lauf rot. -App ral/qualitymarks/hearingportal erzwingt die App-Prüfung; -All prüft CAF und alle ausführbaren Apps zur Vollabnahme.

Der versionierte pre-push-Hook verlangt einen sauberen Arbeitsbaum am gepushten Tipp und bindet sein frisches Ergebnis an alle Ref-Paare. Verschiedene Tipps in einem Push werden abgewiesen. Nur das verifizierte Ergebnis wird als Git-Note refs/notes/quality-gate angeheftet. Reine Doku-/Jira-Änderungen prüfen Links und Reporting; unbetroffene App-Gates sind ausdrücklich NOT_APPLICABLE. Am Commit bleibt nur die Prüfung des gestageten Diffs auf Secrets, gesperrte Pfade und Konfliktmarker.

Die konkrete Nachweismatrix je Änderungsart (Contract/Permission, Domainregel, geschützter Read/Write, Concurrency, EF-Migration, Dokument-/Portalpfad, Blazor-Komponente, Skript, Dokumentation) steht im Entwicklungsleitfaden §15.

Weiterführend

  • Bausteinsicht — Modulgrenzen und Abhängigkeitsrichtung
  • Laufzeitsicht — Requestablauf, Dokumentzugriffswege, Hearing-Lebenszyklus
  • Architekturentscheidungen — begründete Grundsatzentscheidungen
  • ../DEVELOPMENT-GUIDE.md — normative Regeln je Änderungsart
  • ../GETTING-STARTED.md — lokaler Stack und Betriebseinstieg
  • ../../conventions/ — themenspezifische Coding-Konventionen

8. Störungen und Betriebsbereitschaft (RAL-QM-HP-37)

Der fachliche Vertrag steht in SPEC-RAL-QM-HP-37. Hearings besitzt RALQMHearingIncidents, deren Versuchshistorie in RALQMHearingIncidentFailures und RALQMHearingIncidentNotifications. Quellart und Quell-ID identifizieren einen Vorgang eindeutig; ein erneutes Bereitstellen erledigt keinen Fehler. Dieselbe Quellart Notification trägt seit RAL-QM-HP-25 auch die Zustellfehler der Freigabe-Benachrichtigung (RALQMHearingApprovalDecisionNotifications, Vorgangsart Notification:ApprovalDecision, Codes notification-failed und notification-recipient-missing); technische Fehler werden weiterhin wiederholt. Nach 72 Stunden seit dem ersten Empfängerfehler pausieren Entscheidungsaufträge zur fachlichen Klärung (RAL-QM-HP-69, ADR-09). Die Geschäftsführung bestätigt einen verknüpften Folgeauftrag; Originalempfänger und Versuche bleiben erhalten. Der pausierte Auftrag wird nicht automatisch erneut versucht, und der Detector behandelt ihre Tabelle wie die Störungsbenachrichtigungen (verwaister Claim → process-interrupted, Erledigung mit dem Versandzeitpunkt). Erst der erfolgreiche Abschluss setzt den Erledigungszeitpunkt. Endfehler erhalten einen persistenten, pro Verfahren/Vorgangsart/Fehlercode gebündelten Benachrichtigungsauftrag. Der Benachrichtigungsfehler selbst unterdrückt weitere Alarmnachrichten und bleibt nachholbar. Ein separater periodischer Detector erkennt ausbleibende Ergebnisse auch dann, wenn der Dispatch-Pass selbst auf einen Provider wartet. Er gleicht außerdem gespeicherte Quellergebnisse mit der Störungssicht ab, um einen Prozessabbruch zwischen beiden Schreibschritten nachzuholen. Ein lokal noch laufender Versand wird nach Timeout für ein weiteres StuckAttemptTimeout beobachtet und nicht parallel neu gestartet; antwortet der Transport auch dann nicht, wird der Versuch als unterbrochen abgeschlossen und die Nachricht regulär wiederholt. Jede Benachrichtigungszustellung läuft in einer eigenen Unit of Work, damit ein an einen parallelen Worker verlorener Claim die übrigen Zustellungen des Durchlaufs nicht blockiert. Der Fristenprozessor speichert Fehlzustand und Störungshistorie gemeinsam in seiner Fehlertransaktion; die erfolgreiche Ausführung erledigt die Störung in derselben Transaktion. Altfehler werden ohne nachträgliche Alarmflut importiert. Die UI kennzeichnet unvollständige Althistorien und ersatzweise verwendete Zeitpunkte des letzten bekannten Quellzustands.

Authorization löst aktive interne Empfänger über einen schmalen Contract auf; es gibt keine fremden Identity-Repositories im Hearings-Feature. Administrator und Referent erhalten die getrennten Rechte zum Lesen/Wiederholen, Geschäftsführung erhält das Leserecht. Es gilt dieselbe verfahrensübergreifende interne Sicht wie bei der vorhandenen Verfahrensliste.

Die technische Zustandsauskunft bleibt im Host. Regelmäßige ASP.NET-Health-Probes prüfen Datenbank, Dateiablage, SMTP und weiterhin Keycloak. Datei-Probes verwenden ausschließlich einen synthetischen Marker im bestehenden BLOB-Container; SMTP-Probes senden keine E-Mail. Ein In-Memory-Snapshot enthält nur freigegebene Betriebsmittelkennungen, Status und Prüfzeit. Ein veralteter oder fehlender Snapshot ist nicht bereit. Zustandswechsel gehen in das bestehende strukturierte Betriebslog, damit ein Datenbankausfall den Protokollpfad nicht voraussetzt.

/health/live bleibt vom Zustand der Abhängigkeiten unabhängig, /health/ready gibt weiterhin nur den Gesamtstatus aus. /health/status ergänzt die datensparsame Einzelsicht und prüft die direkte Client-IP gegen eine konfigurierte CIDR-Allowlist (standardmäßig Loopback). Sie verlangt keine DB-abhängige Authentifizierung. Bei Reverse-Proxy-Deployment ist eine zusätzliche Sperre oder Netzwerkfreigabe am Proxy erforderlich; die Loopback-Adresse des Proxys ist kein Nachweis für einen berechtigten externen Client. Der Deploy-Stack sperrt die Route im Caddyfile über OPS_ALLOWED_CIDR und reicht die Client-IP per X-Forwarded-For an die Anwendungs-Allowlist durch. Konfiguration und noch ausstehender RAL-Betriebsnachweis stehen in der Spec; Quellcode allein belegt keine produktive Einsatzbereitschaft.

Institutionsimport und interne Notizen (RAL-QM-HP-49)

Institution.InternalNote ist eine optionale nvarchar(2000)-Spalte auf RALQMInstitutions. Die bestehende RAL-QM-HP-10-Migration besitzt diese Spalte; RAL-QM-HP-49 ergänzt keine zweite Notiz. Das Aggregat begrenzt ihre Länge; die bestehenden Create-/Update-Permissions und der optimistische Concurrency-Stamp schützen auch Notizänderungen. Öffentliche Institutionsreferenzen, Kontakte und Portalverträge erhalten kein Notizfeld.

ABPs DisableAuditing schließt die Notiz aus den betreffenden DTO-Parametern und aus der Entity-History aus. Dasselbe gilt für InstitutionImportInput.FileContent: Die importierte Datei wird nicht als Action-Parameter protokolliert. Die ABP-Auditaktionen und zulässige Entity-Änderungen bleiben erhalten. SQL-/ABP-Proxy-Tests prüfen sowohl die fehlenden Inhalte als auch die weiterhin vorhandenen Auditereignisse.

Der CSV-Import ist create-only und validiert alle Kontakte vor dem Insert des Aggregats. Bekannte Namen werden getrimmt und ohne Beachtung der Groß-/Kleinschreibung erkannt; aufeinanderfolgende Wiederholungen und Dubletten innerhalb einer Datei werden übersprungen. Eine globale Namenseindeutigkeit bei konkurrierenden Neuanlagen wird damit nicht eingeführt. Der Export nutzt die Listenfilter ohne Paging und liefert die interne Notiz ausschließlich nach ausdrücklicher Wahl für die einzelne Exportaktion. Der genaue Dateivertrag steht in SPEC-RAL-QM-HP-49.

Der Kontaktteil der Datei trägt seit Teil 2 zusätzlich Anredeform, Titel, Telefonnummer und das Standardkennzeichen. Er erzeugt keine neue Spalte in der Datenbank: alle vier Felder bestehen seit RAL-QM-HP-9 und RAL-QM-HP-26, und es entsteht keine Migration. Das Standardkennzeichen steht je Kontaktblock und genau einmal je Zeile. Der gefilterte eindeutige Index bleibt dabei unberührt, weil die Institution samt Kontakten als ein Insert geschrieben wird, in dem genau eine Kontaktzeile das Kennzeichen trägt; MarkAsMandatoryPartner fasst ohnehin keine Kontaktzeile an. Die Vertretung bleibt außerhalb der Datei, weil ihre Zuweisung einen aktiven Vertreter verlangt und der Import alle Kontakte aktiv anlegt — die Invariante wäre auf diesem Pfad unerreichbar.

09. Architekturentscheidungen

Dieses Kapitel hält die wesentlichen, dauerhaft wirksamen Architekturentscheidungen in ADR-Kurzform fest: Titel, Kontext, Entscheidung, Konsequenz. Details zu Bausteinen stehen in der Bausteinsicht, zu Qualitätsanforderungen in der Qualitätsanforderungen bzw. dem Qualitätsbaum. Die vollständige, laufend gepflegte Konventions-Bewertung inkl. Vor-/Nachteilsabwägung steht in docs/conventions-decisions.md.

ADR-01 · No-Layer-ABP statt klassischer 8-Projekt-Schichtung

  • Kontext: ABP-Projekte werden üblicherweise in Domain, Application, EntityFrameworkCore, HttpApi, Blazor usw. je Modul zerlegt. Für einen modularen Monolithen mit überschaubarer Modulzahl erzeugt das viel Projekt-/Verkabelungs-Overhead ohne echten Nutzen.
  • Entscheidung: Jedes Fachmodul besteht im Normalfall aus höchstens drei Assemblies: *.Contracts (DTOs, Interfaces, Permissions, Localization, öffentliche IDs), einer Runtime (Domain, Application, EF-Mapping in einer Assembly) und optional *.Blazor (Feature-UI). Domain, Application und Persistence bleiben logische, aber nicht separat materialisierte Schichten. Eine weitere Assembly entsteht erst, wenn sie unabhängig konsumiert, paketiert, ersetzt oder betrieben werden muss.
  • Konsequenz: Weniger Projekte/Referenzen, schnellere Builds, geringere Einstiegshürde. Die Abhängigkeitsrichtung (Contracts ↔ Runtime ↔ Blazor, keine Rückkanten) bleibt trotzdem eine Compile-Time-Eigenschaft, durchgesetzt durch Architekturtests statt durch Projektgrenzen allein (siehe ADR-06). Konvention ABP-11 in conventions-decisions.md markiert die klassische 8-Schichtung entsprechend als N/A für dieses Repository.

ADR-02 · Monorepo mit Host + wiederverwendbaren CAF-Modulen + einer RAL-Anwendung

  • Kontext: CAF.Modules.* (Authentication, Documents[.Contracts], UI.Blazor) sind anwendungsneutrale Bausteine; HearingPortal war zum Entscheidungszeitpunkt die einzige existierende Anwendung, die sie konsumiert. Getrennte Repositories hätten Versions-Ping-Pong zwischen Framework und Anwendung erzwungen, obwohl beide im selben Entwicklungstakt reifen.
  • Entscheidung: Ein Repository (CAF.slnx) bündelt modules/ (CAF-Bausteine) und applications/RAL/QualityMarks/HearingPortal/src/ (Host, Shell, Contracts, Features). Die Ordnerstruktur der beiden Solutions führt die Bausteinsicht §5.1.
  • Konsequenz: Ein Commit kann Framework und Anwendung atomar ändern; Refactorings über die Grenze sind ohne Cross-Repo-Release möglich. Im Gegenzug muss die Modulgrenze CAF.Modules.* → Applications.* verboten bleiben, sonst verwischt das Monorepo den eigentlichen Zweck der Module (wiederverwendbar, anwendungsneutral) — durchgesetzt in ADR-06. Die vier CAF-Bausteine bleiben dabei bewusst Quellcode-Bausteine und werden nicht paketiert (ADR-03); der Host konsumiert sie ausschließlich per ProjectReference.

ADR-03 · CAF-Bausteine als Quellcode-Bausteine mit Vertragsdatei (VERTRAG.md)

  • Kontext: CAF.Modules.Authentication, CAF.Modules.UI.Blazor, CAF.Modules.Documents und CAF.Modules.Documents.Contracts sollen wiederverwendbar bleiben, ohne dass Konsumenten sich auf undokumentierte interne Details verlassen. Eine frühere Fassung dieser Entscheidung sah echte NuGet-Pakete vor (IsPackable=true, eigene PackageId/Version, Pack-Skript). Das wurde zurückgenommen: es gab keinen Konsumenten außerhalb dieses Repositorys, also war es Paketinfrastruktur ohne Abnehmer — mit Versionspflege, Feed-Frage und NU5104-Rauschen als laufenden Kosten.
  • Entscheidung: Die Bausteine sind Quellcode-Bausteine, keine NuGet-Pakete. Jedes der vier csproj setzt IsPackable=false und trägt keine PackageId, keine Version und kein PackageReadmeFile; der Host konsumiert sie ausschließlich per ProjectReference. Die verbindliche Vertragsgrenze bleibt die deutschsprachige VERTRAG.md im Wurzelverzeichnis des jeweiligen Projekts — als Quelltext-Vertrag: zugesicherte öffentliche Fläche, ausdrückliche Nicht-Zusicherungen, Versionierungsregel für den Fall einer späteren Paketierung. Ein Pack-Skript existiert nicht.
  • Konsequenz: Kein Versions-Ping-Pong und kein Feed-Zwang für die tägliche Entwicklung; die Wiederverwendbarkeit ist eine Eigenschaft des Abhängigkeitsgraphen (ADR-06), nicht einer Paketgrenze. Der Vertrag ist trotzdem geschrieben und maschinell verankert: FrameworkPackagingTests pinnt genau vier CAF.Modules-Projekte, deren Nicht-Packbarkeit samt fehlender Paketidentität, und dass jede VERTRAG.md existiert, nicht leer ist und beide Pflichtabschnitte trägt. Ein versehentliches IsPackable=true oder eine gelöschte Vertragsdatei bricht den Build. Preis: Ein Konsument außerhalb dieses Repositorys ist heute nicht bedienbar — er bräuchte zuerst eine erneute Paket- und Feed-Entscheidung. Ein zweites Produkt im Repository funktioniert ohne Änderung.

ADR-04 · RALQM als data-preserving Umbenennung persistierter Kennungen

  • Kontext: Das Produkt hieß im Code zunächst Guetezeichen, dann QualityMarks; die verbindliche Kurzform für persistierte, anwendungsspezifische Kennungen ist inzwischen RALQM (Tabellen RALQMHearings/RALQMInstitutions, Fehlercode-Präfix RALQM.<Feature>:, Permission-Gruppe RALQM, Rollen RALQMAdministrator/RALQMCaseOfficer/RALQMManagement). Technische Slugs (z. B. Docker-/Keycloak-Client-IDs wie ral-qualitymarks) bleiben bewusst lowercase und wurden nicht mitgezogen.
  • Entscheidung: Jede Umbenennung persistierter Bezeichner erfolgt ausschließlich über eine data-preserving EF-Migration (RenamePersistedIdentifiersToRALQM, siehe applications/RAL/QualityMarks/HearingPortal/src/Host/Migrations/) mit korrekt gespiegeltem Down(); historische Migrationen behalten ihre alten, zum jeweiligen Zeitpunkt gültigen Bezeichner (z. B. AddCafDocumentShareGrants) und werden nicht nachträglich editiert (Konvention EF-03). Bestehende Dev-Datenbanken werden über ein eigenständiges, nicht-destruktives Skript migriert (applications/RAL/QualityMarks/HearingPortal/tools/Migrate-DevDatabaseToRalqm.ps1: klont das Docker-Volume, benennt DB/Login auf der Kopie um, Quelle bleibt Rollback-fähig).
  • Konsequenz: Rename-Operationen sind reversibel und laufen ohne Datenverlust, auch auf Anwendungsdatenbanken mit Bestand (verifiziert per Round-Trip apply→revert→re-apply sowie gegen echte Dev-Daten: Tabellen-/User-/Grant-Zahlen vor/nach Migration identisch). Der Preis ist zusätzliche Migrations- und Skript-Pflege statt einer einfachen Such-Ersetzen-Operation im Quellcode.

ADR-05 · Cohesion als verbindliche UI-Bibliothek für Fachcode

  • Kontext: ABP liefert LeptonX/MudBlazor für Account-, Administrations- und Themenseiten mit. Für die eigene Gütezeichen-Fachoberfläche soll es eine einheitliche, kapselnde Komponentenbasis geben statt direkter Vendor-Kopplung in jeder Feature-Komponente.
  • Entscheidung: Eigene CAF-/RALQM-Seiten verwenden ausschließlich Cohesion, zentral über CAF.Modules.UI.Blazor registriert (Lizenzprüfung, Assets/Bundles; Paket aus dem GitLab-Gruppen-Feed, zentrale Floating-Version 3.*-*). MudBlazor, Syncfusion oder Blazorise dürfen von Fachcode nicht direkt gerendert werden; ausgenommen sind allein die MVC-Razor-Pages unter /Account/**, die auf dem LeptonX-MVC-Theme bleiben. Die ABP-Administrationsansichten waren zunächst ebenfalls ausgenommen und sind es nicht mehr: sie sind durch Produktseiten ersetzt, und die Blazor-Vendor-Pakete sind entfernt. Nur Interactive Server wird unterstützt, kein WebAssembly; Feature-Komponenten bleiben render-mode-neutral (kein @rendermode im Feature-UI).
  • Konsequenz: Vendor-Details sind in Wrappern gekapselt, ein späterer UI-Bibliothekswechsel bliebe lokal begrenzt. Fehlende generische UI-Primitives gehören in die separat versionierte Cohesion-Bibliothek, nicht in Fachcode-Workarounds. Die Regel ist kein Stilhinweis, sondern arch-test-erzwungen (BL-01/BL-02 in conventions-decisions.md).

ADR-06 · Strikte Modulgrenzen durch Architekturtests statt Konvention allein

  • Kontext: Ordnerstruktur und Namensschema allein verhindern in der Praxis keine Querabhängigkeiten — insbesondere unter Zeitdruck oder bei automatisiertem/agentischem Edit. Der No-Layer-Ansatz (ADR-01) und das Monorepo (ADR-02) erhöhen dieses Risiko zusätzlich, weil alle Module im selben Prozess und Repository liegen.
  • Entscheidung: Die Abhängigkeitsrichtung ist explizit tabelliert (siehe Bausteinsicht) und über Architekturtests erzwungen, u. a.: CAF.Modules.* → Applications.* verboten, Contracts referenzieren nie Runtime/EF/Host, Feature-Blazor referenziert nie die eigene oder eine fremde Runtime, GZ-Runtime referenziert nie fremde Runtime/Entities/Repositories/DbSets/Tabellen, kein Modul referenziert den ausführbaren Host, keine neue Fachlogik/Fachentity im Host. Über Modulgrenzen werden nur IDs gespeichert, nie Cross-Module-Foreign-Keys oder -Navigation (z. B. InstitutionId/DocumentId statt Entity-Referenz).
  • Konsequenz: Grenzverletzungen sind Build-Fehler, nicht Review-Findings. Renames und Restrukturierungen (z. B. die Gruppierung der Feature-Slices unter applications/RAL/QualityMarks/HearingPortal/src/Features/<Feature>/ oder die RALQM-Umbenennung) dürfen laut Projektregel die Grenzregeln selbst nie lockern, sondern ausschließlich Pfad-Literale in den Tests nachziehen. Das hält die Compile-Time-Eigenschaft auch über viele aufeinanderfolgende Strukturänderungen hinweg stabil.

ADR-07 · Wertelisten als Katalogtabellen statt Enum

  • Kontext: Kennzeichnungsart und Verfahrensart waren C#-Enums, als int auf RALQMHearings gespeichert und über Localization-Schlüssel (Hearings:MarkType:*, Hearings:ProcedureType:*) beschriftet; Land und Funktion eines Ansprechpartners waren Freitextspalten ohne Liste. Ein persistierter Enum-Wert ist ein Migrationsvertrag: Die Zahl steht in jeder Zeile, eine neue Ausprägung oder ein geänderter Anzeigename bedeutet einen Quelltext-Release, und ein entfernter Wert lässt Bestandszeilen zurück, die keinen Namen mehr auflösen. Der Fachbereich verlangt für alle vier Listen Pflege durch den Systemadministrator über die Oberfläche und zusätzlich Standardfristen je Verfahrensart — Begleitdaten, die ein Enum überhaupt nicht tragen kann (SPEC-RAL-QM-HP-4, FR-PV-13/FR-PV-16).
  • Entscheidung: Vier Katalogtabellen (RALQMMarkTypes, RALQMProcedureTypes, RALQMCountries, RALQMContactFunctions), ein Aggregat je Liste im eigenen Feature-Slice ValueLists. Jede Ausprägung trägt eine unveränderliche Guid-Kennung und einen stabilen technischen Code; fremde Module speichern ausschließlich die Kennung, ohne Foreign Key. Anzeigenamen liegen je Kultur am Datensatz (NameDe Pflicht, NameEn optional, Rückfall auf Deutsch), nicht als Localization-Schlüssel. Ausprägungen werden nie gelöscht, nur deaktiviert; eine deaktivierte bleibt an Datensätzen sichtbar, die sie bereits tragen, und wird für neue nicht mehr angeboten. Es gibt keinen Cache. Die Verfahrensart trägt optional die drei Standardfristen (erste und zweite Rückmeldefrist, Abschlusserinnerung) als Owned Value Object in ihrer eigenen Tabelle.
  • Konsequenz: Die Compile-Time-Sicherheit des Enums ist bewusst aufgegeben — kein vollständigkeitsgeprüfter switch über die Ausprägungen, kein Kompilierfehler, wenn eine neue hinzukommt. An ihre Stelle treten Laufzeitprüfungen im Aggregat und im Anwendungsdienst (EnsureSelectableAsync, Fachfehler statt stiller Annahme). Zweiter, konkreterer Preis: Die Sortierung der Verfahrenslisten nach Verfahrensart entfällt serverseitig, weil der Anzeigename jetzt in einer anderen Tabelle liegt und ein Join über die Modulgrenze verboten ist; HearingListSortQuery führt das Feld nicht mehr, eine Anfrage darauf fällt auf die Standardsortierung zurück statt zu scheitern, und die Grid-Spalte ist als nicht sortierbar markiert. (Nach der Kennzeichnungsart ließen sich die Listen ohnehin nie sortieren.) Der CSV-Listenexport schreibt seither unter den unveränderten Spaltenköpfen MarkType/ProcedureType die aufgelösten Anzeigenamen — kulturabhängig und jederzeit umbenennbar, ohne stabile Id-Spalte zum Rückfallen; bewusst offengelassen, siehe Spec. Weil Ausprägungen nur deaktiviert werden, wächst der Katalog dauerhaft — auch um Einträge, die nie benutzt wurden; das ist der Preis dafür, dass kein Bestandsdatensatz je auf eine gelöschte Kennung zeigen kann. Der Umstieg kostete eine mehrstufige Expand/Contract-Migration (siehe Querschnittliche Konzepte §2), und die Seed-Ids sind seitdem selbst ein Vertrag: ValueListSeedIds wird aus den Demo-Daten und aus knapp dreißig Testdateien referenziert, ein geänderter Wert bricht sie alle.
  • Verworfene Alternativen:
  • Enum behalten und eine Overlay-Tabelle nur für Namen und Fristen führen. Hätte die Compile-Time-Sicherheit gerettet, aber genau die Anforderung verfehlt: Eine neue Ausprägung bliebe ein Quelltext-Release. Für Land und Funktion gibt es zudem gar kein Enum, das man überlagern könnte — es wären zwei verschiedene Mechanismen für vier gleichartige Listen entstanden, plus zwei Wahrheitsquellen je Ausprägung, die auseinanderlaufen können.
  • ABP Language Management für die Anzeigenamen. Language Management verwaltet Ressourcentexte, die ein Entwickler ausliefert und ein Übersetzer nachzieht. Die Namen hier sind das Gegenteil: Daten, die ein Administrator anlegt und ändert. Jede neue Ausprägung hätte einen Ressourcenschlüssel gebraucht, den niemand ausgeliefert hat, und der Name wäre vom Datensatz getrennt an einem zweiten Ort gelandet — mit der Löschung des einen ohne die des anderen als naheliegendem Folgefehler. Die Spec schließt diesen Weg ausdrücklich aus; deshalb NameDe/NameEn am Datensatz (siehe Querschnittliche Konzepte §4).

Lokale Stack-Isolation (CAF-REPO-03)

Persistente Entwicklungsressourcen gehoeren einer App, einem kanonischen Worktree, einem vollstaendigen Branch-Ref und einer Betriebsart. Ein lesbarer Name mit Identitaetshash, ein gemeinsamer Descriptor und Ressourcenlabels verbinden Lifecycle, F5 und Endpunkte. Eigene Run-Stacks isolieren BrowserGates; Testcontainers 4.15.0 mit Ryuk bereinigt SQL-/SMTP-Testfixtures. Persistente Daten werden nicht automatisch bereinigt. Details und Abnahmen: SPEC-CAF-REPO-03.

Diese Trennung benoetigt mehr RAM und Startzeit. Gate und Clean Room begrenzen die gleichzeitig getesteten Projekte auf hoechstens zwei; der Clean Room kann mit -MaxParallelProjects 1 seriell laufen. Die fachlichen Parallelitaetstests bleiben unveraendert. Mehrere Vollpruefungen sollten auf einer Workstation zeitlich koordiniert werden. Ein gemeinsamer Stack oder eine Bereinigung anhand von Alter und Namenspraefix wuerde fremde Daten und Nachweise gefaehrden.

RAL-QM-HP-52: Inaktivität im vorhandenen Authentifizierungsmodul

Entscheidung vom 14.09.2026: Die freigegebenen 15 Minuten Inaktivität und acht Stunden absolute Dauer werden durch einen optionalen, prozesslokalen Sitzungszustand im vorhandenen CAF-Authentication-Modul durchgesetzt. ABP-Account/OIDC bleiben der Loginpfad; keine zweite Identitäts- oder Sitzungsdatenbank. Cookie-Sliding allein scheidet aus, weil es automatischen Verkehr als Aktivität behandeln und offene Circuits nicht vor ihrer nächsten Aktion abweisen würde.

Vorteil: HTTP, offene Circuits und mehrere Tabs nutzen denselben Widerrufszustand, ohne neue Infrastruktur. Nachteil: Prozessneustart beendet alle Sitzungen; mehrere Hostinstanzen sind damit nicht freigegeben. Das ist für den vorhandenen einzelnen Interactive-Server-Host bewusst akzeptiert. Institutionsbindung und unmittelbarer Entzug nach Kontomutation liegen im Vertrag von RAL-QM-HP-18; CAF liefert dafür nur den generischen Widerruf nach Subject.

RAL-QM-HP-18: Verwaltungsrolle ohne Fallarbeit, exakter Grant-Abgleich, Widerruf nach Subject

Entscheidung vom 15.09.2026:

  1. Der Systemadministrator ist eine Verwaltungsrolle. Seine Grant-Komposition enthält keine Verfahrens- und Freigaberechte mehr; Storno, Versand und Nachversand wandern zum Referenten, die Nachversand-Freigabe zur Geschäftsführung. Alternative war ein zusätzliches „Verwaltungsadministrator“-Profil neben einem Vollzugriffs-Administrator; verworfen, weil Jira (US-04) genau eine Rolle „Systemadministrator“ ohne Fallarbeit verlangt und ein zweites Vollzugriffsprofil die Vier-Augen-Regel aushöhlen würde. Das ABP-Ersteinrichtungskonto behält seine technische Rolle admin (Betriebszugang, App-Leitlinien); die Zusicherung gilt für Konten mit der Produktrolle.
  2. Der Seed gleicht Grants exakt ab. Additive Seeds ließen entfernte Rechte in bestehenden Datenbanken stehen; deshalb entzieht HearingPortalDomainRolesDataSeedContributor je Produktrolle alles, was über die Rollen-Provider-Grants hinaus gehalten wird. Nur die vier Produktrollen werden abgeglichen, nie fremde Provider oder die technische Rolle.
  3. Institutionskonten sind eine vierte Produktrolle mit Bindung. Statt eines Kontos „ohne Rolle“ trägt ein Institutionskonto RALQMInstitution (nur Portalrecht) und eine eigene Bindungstabelle an Institution und Ansprechpartner. So bleibt „genau eine Rolle je Konto“ invariant, die Kontoart ist aus der Rolle ableitbar (RoleKindMismatch verhindert Wechsel zwischen den Welten), und der Portal-Konsum der Bindung (RAL-QM-HP-30/RAL-QM-HP-31) hat einen Ort.
  4. Kein Löschen, Papierkorb. Konten werden per Soft Delete aus der Sicht genommen und sind wiederherstellbar; der Vertrag kennt keinen Delete-Pfad. Bindung, Verlauf und Verfahrensbezüge bleiben erhalten.
  5. Sitzungswiderruf nach Principal in CAF, Fachlogik im Produkt. CAFSessionStore.RevokeSubject ist generisch (welche Sitzungen enden; Schlüssel ist der NameIdentifier des Cookies, mit ABP Identity die lokale Benutzer-Id); wann er ausgelöst wird (Sperre, Kennungswechsel, Papierkorb) entscheidet der Kontodienst über den Host-Port IAccountSessionRevoker.RevokeUser. Die Sperre wirkt dreifach — Keycloak enabled=false plus Provider-Logout, Security Stamp, CAF- Sitzung —, weil ein einzelner Mechanismus (nur ABP-Stamp alle fünf Minuten) die geforderte Wirkung „bei der nächsten Aktion“ nicht garantiert.
  6. Einladungszustand ohne eigene Tabelle. Sendezeitpunkt als ExtraProperty des Benutzers, Annahme aus dem Keycloak-Zustand; ein nicht erreichbarer Provider degradiert zu „unbekannt“. Keycloak-Action-Tokens sind nicht widerrufbar — die Lebensdauer von 7 Tagen ist die Grenze (Spec O3).

Details, Akzeptanzkriterien und Nachweise: SPEC-RAL-QM-HP-18.

ADR-08 · Operativer Empfängerstand im Portal und geschützte Erinnerungsunterlagen

Status: angenommen durch Benutzerentscheidung vom 29.09.2026.

Kontext: SPEC-RAL-QM-HP-6 und SPEC-RAL-QM-HP-30 nannten unterschiedliche Anschriftenstände für das Portal. SPEC-RAL-QM-HP-33 verlangte zugeordnete Unterlagen in Erinnerungen, ohne deren Übertragungsform festzulegen.

Entscheidung: Das Portal zeigt den operativen Empfängerstand der Beteiligung. Änderungen der Stammdaten wirken erst nach ausdrücklicher Übernahme; diese Übernahme wird sofort im Portal sichtbar. Jede Aussendung behält ihren eigenen unveränderlichen Empfängerbeleg. Erinnerungen verweisen auf die der Institution zugeordneten Unterlagen über die bestehenden authentifizierten, tokenfreien Portalrouten. Der Download prüft bei jedem Aufruf Bindung, Teilnahme und Grant.

Der lokale Transportnachweis verwendet den bestehenden Mailhog-Stack (vom Benutzer als „Mailhawk“ bezeichnet). Eine spätere Exchange-SMTP-Anbindung und authentifizierte Rückmeldungen für Bounces bleiben im Integrationsumfang von SPEC-RAL-QM-HP-24 offen; SMTP-Annahme ist kein Empfangsnachweis. Es wird kein produktiver Anbieter oder Callback-Vertrag vorweggenommen.

Konsequenz: Portal und neu vorbereitete Nachrichten verwenden denselben operativen Stand; historische Belege ändern sich nicht. Erinnerungslinks verleihen kein Zugriffsrecht und bleiben nach Grantentzug gesperrt. SPEC-RAL-QM-HP-23 AC-4 bleibt hinsichtlich Bounces ausdrücklich teilweise erfüllt.

ADR-09 · Freigabe, Empfänger, Erinnerungen und Übergabe

Status: angenommen durch Quizantworten und Präzisierungen des Benutzers vom 30.09.2026. Umsetzung und Nachweise stehen in den bestehenden Specs zu RAL-QM-HP-68, RAL-QM-HP-69, RAL-QM-HP-70, RAL-QM-HP-71, RAL-QM-HP-72.

  1. Die Geschäftsführung darf ein noch nicht versandtes Verfahren aus Approved nach Draft zurücknehmen, um einen dauerhaft ungültigen Empfänger zu korrigieren. Frühere Entscheidungen und Hashes bleiben erhalten; die aktuelle Freigabe wird entwertet. Erneute Einreichung und Vier-Augen-Freigabe sind Pflicht. Die Rücknahme verlangt keine Wartefrist.
  2. Bei internen Entscheidungsbenachrichtigungen beginnen 72 Stunden mit dem ersten fehlgeschlagenen Empfängerabgleich. Danach pausiert der Auftrag zur fachlichen Klärung, ohne weitere automatische Versuche. Technische SMTP-Fehler behalten ihre bisherigen Retries. Die Geschäftsführung bestätigt eine Fortsetzung an den wieder erreichbaren ursprünglichen oder den ausdrücklich ausgewählten aktuellen Zuständigen. Ein verknüpfter Folgeauftrag trägt Empfänger, Akteur und Vorgangskennung; der ursprüngliche Auftrag bleibt unverändert.
  3. Automatische Erinnerungen, die erst nach dem wirksamen individuellen UTC-Fristtag ausgeführt werden, enden ohne Nachricht mit „Frist bereits abgelaufen“. Ihre frühere Fälligkeit erlaubt keinen verspäteten Versand. Der geschützte Bericht zeigt die Einreihung sofort, SMTP-Übergaben, Fehlschläge und offene Aufträge getrennt. Eine kurze E-Mail verweist auf ihn nach Abschluss aller Aufträge, spätestens nach 24 Stunden als ausdrücklich unvollständiger Zwischenbericht. Dies ist keine Retry-Grenze. Manuelle Auswahl bleibt über Seiten erhalten, wird vor dem Auftrag bestätigt und für jedes gewählte Ziel erneut am Server geprüft.
  4. Pro Institution bleibt eine Beteiligung und eine Stellungnahme. Der Standardkontakt wird vorgeschlagen; weitere aktive Kontakte derselben Institution werden ausdrücklich gewählt. Genau ein organisatorischer Primärempfänger bleibt Quelle des operativen Portalstands. Alle Gewählten erhalten alle relevanten Nachrichten. Personenbezogene Kontobindung und Autorisierung bleiben unabhängig davon. Hauptempfänger müssen unterschiedliche Adressen haben. Vertretungskopien gelten je gewähltem Kontakt: keine Kopie an dessen eigene Adresse, aber mögliche Mehrfachkopien für mehrere vertretene Kontakte, auch an andere Hauptempfänger.
  5. Reguläre Kontodeaktivierung verlangt vorherige vollständige Übergabe offener Zuständigkeiten. Eine ausdrücklich dringende Kontosperre bleibt möglich. Unerwartet verwaiste Verfahren stehen in einer separaten Klärungsliste der Geschäftsführung. Deren persönlicher Arbeitsvorrat bleibt auf PendingApproval begrenzt; weitere Leitungsaufgaben aus RAL-QM-HP-46/RAL-QM-HP-51 sind separat zu entscheiden. Die bestehende Anlageberechtigung und Zuständigkeit des aktiven internen Erstellers bleiben erhalten. Nur das ausdrücklich gewählte Übergabeziel muss aktiver Referent sein; eine aktive Geschäftsführung als Ersteller ist allein wegen ihrer Rolle kein verwaister Zuständiger.

Konsequenzen: Hearings besitzt zusätzliche Empfängerstände, Berichte und fachliche Klärung. Authorization besitzt Kontoeignung und Sperre; beide kommunizieren über schmale Contracts. Historische Empfänger und Aufträge werden weder still neu adressiert noch durch Stammdaten ersetzt. RAL-QM-HP-73, RAL-QM-HP-74, RAL-QM-HP-75 sind technische Korrekturen; der Pager-Behelf entfällt erst nach gemessenem Paketfix.

ADR-10 · Antwortfolge lesend vor dem gemeinsamen Abgabeabschluss vorbereiten

Status: angenommen für den angeforderten Implementierungsstart RAL-QM-HP-40 am 29.09.2026; technischer Teilschnitt, am 30.09.2026 nach Integration von RAL-QM-HP-38 um den unten beschriebenen Schreibwechsel erweitert. Keine Freigabe der offenen Rechtstexte aus RAL-QM-HP-39.

Kontext: Der bestehende Abgabeweg speichert eine Antwort vor deren Anlagen und erlaubt danach Änderungen. RAL-QM-HP-38/RAL-QM-HP-39 müssen Anlagen und versionierte Zustimmung atomar mit dem finalen Abschluss verbinden. Eine vorgezogene Unveränderlichkeit würde den einzigen Uploadweg sperren; eine vorgezogene Indexöffnung ließe alte Schreiber beliebige Zeilen ändern.

Entscheidung: Zuerst positive Sequenz, kompatible Antwortliste und deterministische Auswahl des aktuellen Gesamtstands auf dem bestehenden Aggregat implementieren. Altzeilen erhalten Sequenz 1. Beide Unique-Indizes bleiben auf aktive Zeilen gefiltert, der bisherige Einzelantwortindex bleibt zusätzlich bestehen. Leser liefern alle eigenen Beiträge und verwenden für Kategorieentscheidungen nur den letzten je Teilnahme. Legacy-Schreiber weisen mehrere Antworten bzw. eine alleinige spätere Sequenz ab. Keine wirksame Frist sperrt das Fenster gemäß dem bestehenden Fachvertrag.

Konsequenzen: Bestehende Anlagen bleiben nutzbar; Leser und Regressionen sind vor dem Schreibwechsel vorbereitet. Bis zum gemeinsamen Abschluss ist genau eine aktive Antwort persistierbar und weiterhin über den Legacy-Weg änderbar. Zusätzlicher Index und kompatibles Response-Feld sind Übergangskosten. Ergänzungsendpunkt, Operation-ID/Retry, atomare Sequenzvergabe, Zustimmung und Persistenzschutz werden gemeinsam nachgeführt; die Story bleibt teilweise offen. Details und Nachweise: SPEC-RAL-QM-HP-40.

Fortführung nach Integration von RAL-QM-HP-38 (30.09.2026): Der atomare Anlagenabschluss ist jetzt vorhanden. Die aktuelle RAL-QM-HP-38-Spec ordnet Zustimmung und Unveränderlichkeit jeweils RAL-QM-HP-39 bzw. RAL-QM-HP-40 zu. Der angeforderte Ausbau von !155 liefert deshalb den technischen Schreibwechsel: Erstabgabe und explizite Ergänzung mit eigenem Inhalt, serverseitige Sequenz unter Teilnehmerreservierung, akteur- und inhaltsgebundene Auftragswiederholung sowie Festschreibung gegen Änderungen und Löschung. Der Einzelantwortindex wird bei diesem gemeinsamen Wechsel auf Sequenz 1 begrenzt; der alte Änderungsendpunkt weist deterministisch ab. Historische Antworten und Dateien bleiben erhalten. Leser verwenden weiterhin den jüngsten Gesamtstand.

Verbleibende Grenze: RAL-QM-HP-39 besitzt weiterhin Textkatalog, fachliche Freigabe, zwei ausdrückliche Bestätigungen und deren Nachweis pro Abgabe. Das wird weder durch ein automatisches Häkchen noch durch erfundene Texte ersetzt. Die vollständige Zustimmungsaussage von RAL-QM-HP-40 AC-3 bleibt offen; die technische Ergänzung wird getrennt davon nachgewiesen. Die Stufengrenze ist damit eine Liefergrenze und keine Behauptung, die rechtlichen Voraussetzungen seien bereits erfüllt.

ADR-11 · Zustimmungen als eigenes Feature-Modul Consents

Status: angenommen am 01.10.2026 (Product Owner, Festlegungen F2–F4 in SPEC-RAL-QM-HP-39). Die Anforderung selbst ist von RAL nicht bestätigt (F1); Inhalt und Freigabe der Texte bleiben dort.

Kontext: Zwei Kontaktpunkte verlangen eine nachweisbare Bestätigung derselben zwei Texte: die Annahme eines Institutionskontos (Authorization) und jede Abgabe (Hearings). Die Texte brauchen Fassungen mit Gültigkeitsdatum und eine Pflegeoberfläche. Keycloak besitzt Passwort und E-Mail-Bestätigung; seine Required-Action „Terms and Conditions“ kennt weder Fassungen noch zwei getrennte Bestätigungen und speichert den Nachweis nur als Benutzerattribut beim Anbieter.

Entscheidung: Ein sechster fachlicher Slice Features/Consents besitzt Fassungskatalog, Pflege, Nachweise und den Aktivierungsschritt. Authorization verlangt in seiner einzigen Zugangsprüfung zusätzlich den Aktivierungsnachweis und bietet für den Zustimmungsschritt den Aktivierungskandidaten an (alle Bedingungen außer dem Nachweis). Hearings schreibt den Abgabenachweis über einen In-Process-Port in der umgebenden Unit of Work. Die Zustimmungsseite liegt im Portal (Hearings Blazor), der gemeinsame Bestätigungsbaustein in Produkt UI.Blazor. Fassungen und Nachweise sind reine Einfügetabellen; Ändern und Löschen werden abgewiesen.

Konsequenzen: Ein Nachweis ist genau dann vorhanden, wenn Aktivierung bzw. Abgabe gelungen ist, weil er in derselben Datenbanktransaktion entsteht. Authorization und Hearings erhalten je eine Kante auf Consents.Contracts; Consents selbst kennt nur Authorization-Contracts. Ohne veröffentlichte Fassung sind Aktivierung und Abgabe gesperrt, also muss der Betrieb nach dem Deployment zuerst beide Texte veröffentlichen. Bestandskonten werden beim nächsten Portalaufruf zur Zustimmungsseite geführt. RAL-QM-HP-31 AC-1 („ohne weiteren Freischaltschritt“) gilt seitdem mit genau diesem einen Schritt. Löschfristen der Nachweise sind offen (Datenschutzkonzept, RAL-QM-HP-67).

ADR-12 · Bestandsabruf der öffentlichen Anzeige mit Keycloak Client Credentials

Status: angenommen durch Benutzerentscheidung vom 01.10.2026 zu RAL-QM-HP-47 (O1–O3).

Kontext: Die Website soll laufende, freigegebene Anhörungen ohne redaktionelle Pflege zeigen. Der Betreiber und sein Übernahmeweg sind nicht bekannt. Bisher hatte die Anwendung keinen eingehenden Maschinenzugang; Keycloak ist der einzige Aussteller.

Entscheidung: 1. Abgleich als Pull: Die Website ruft den Bestand ab (GET /api/gz/hearings/public), voll oder ab einem Cursor mit Änderungen und Entfernungen unter der stabilen öffentlichen Kennung. Ein Push an die Website entfällt, bis ein Betreiber einen Übernahmevertrag nennt. 2. Authentisierung über Keycloak Client Credentials und ein eigenes JWT-Bearer-Schema im Hearings-Runtime, nie Standardschema; Zulassung über Audience und eine Liste erlaubter Clients. Kein statischer API-Schlüssel, keine Übernahme eines alten Klartextschlüssels. 3. Projektion: Titel, Gütezeichen, Verfahrensart, Frist sowie Kennung und Änderungszeitpunkt; keine Institutionen, keine Kurzbeschreibung. Keine Zeitsteuerung: sichtbar, solange gekennzeichnet und Approved/Dispatched. Der Änderungszeitpunkt berücksichtigt die Änderungszeit der Werteliste-Einträge, deren Namen geliefert werden; die Namen sind immer de-DE. Der Abruf läuft hinter der Aufrufbremse hearing-portal (Review !177).

Konsequenzen: Neues Paket Microsoft.AspNetCore.Authentication.JwtBearer im Hearings-Runtime. Der Keycloak-Client wird im Kundenrealm angelegt, wenn der Betreiber feststeht; bis dahin ist der Abruf ausgeschaltet. Übernahmequittung, Störungsmeldung und „letzter erfolgreicher Abgleich“ setzen eine Rückmeldung der Website voraus; sie kamen mit RAL-QM-HP-78 als Quittung zum Pull (ADR-20), samt Seitenbildung des Abrufs.

ADR-13 · Statische Produktfehler ohne Datenbank und Circuit

Kontext: Eine unbekannte Adresse soll mit 404 im Produktlayout antworten, ein technischer Fehler außerhalb von Development mit 500. Die vollständige interaktive Shell lädt Menüs, Rechte, Settings und Übersetzungen aus dem Backend; derselbe Ausfall kann daher auch die Fehlerdarstellung verhindern. Ein erneuter Router-Aufruf würde die fehlgeschlagene Seite wieder öffnen.

Entscheidung (RAL-QM-HP-80, 2026-10-02): Der Host rendert ErrorDocument direkt über RazorComponentResult, mit ShellErrorView und bestehendem Cohesion-/Portal-Template. Nur ausgelieferte DE/EN-JSON-Texte, neutrale Links und die serverseitige Vorgangskennung gehen in diesen Pfad. Die Institutionsrolle wählt ausschließlich die datenfreie Darstellung. HTTP-Status und no-store bleiben erhalten; Maschinenpfade und Account-MVC sind separat.

Konsequenz: Fehlerseiten funktionieren ohne Circuit und ohne Datenbankzugriff. Dynamisch gepflegte Language-Management-Texte erscheinen dort nicht; die shipped ABP-403/404-Wortlaute werden gegen den komponierten Localizer getestet. Serverseitige Fachautorisierung, Cookie- AccessDeniedPath und Framework-Reconnect-Logik bleiben unverändert. Kriterien und Nachweise: SPEC-RAL-QM-HP-80.

ADR-14 · Empfänger ohne Stammdatenkontakt aus einer Kontaktmeldung

Status: angenommen am 02.10.2026 (Abdurrahman Ates, Entscheidung E1 in SPEC-RAL-QM-HP-41).

Kontext: Empfänger einer Beteiligung war bisher immer ein aktiver Kontakt derselben Institution (SPEC-RAL-QM-HP-6, Fachregel 4). Eine Institution kann im laufenden Verfahren eine andere Kontaktperson melden. Soll diese Person nur dieses eine Verfahren betreuen, wäre ein Stammdatenkontakt falsch: Er gälte für jedes künftige Verfahren und füllte die Stammdaten mit Personen, die dort nicht geführt werden sollen.

Entscheidung: Ein operativer Empfängerstand darf ohne Stammdatenkontakt bestehen, wenn er aus einer übernommenen Kontaktmeldung mit dem Ziel „nur dieses Verfahren“ stammt. Hearings schreibt dann Anredeform, Titel, Name, Funktion (als Anzeigename) und E-Mail der Person in den Stand der Beteiligung und leert den Kontaktbezug. Die Kontaktmeldung ist ein eigener Datensatz neben dem Verfahren, kein Kind des Verfahrensaggregats; sie hält Inhalt, Zustand, Entscheidung und Ziel fest. Die Herkunft des Empfängers wird nicht gespeichert, sondern abgeleitet: Die Beteiligung hat keinen Kontaktbezug, und ihre jüngste übernommene oder ersetzte Meldung hat das Ziel „nur dieses Verfahren“. Der andere Weg bleibt die Übernahme in die Stammdaten: Institutions legt über den schmalen Vertrag „einen Kontakt hinzufügen“ einen Kontakt an, Hearings wechselt den Empfänger über den vorhandenen Empfängerwechsel – beides in einer serialisierbaren Transaktion, alle Prüfungen vor dem ersten Speicherschritt.

Konsequenzen: Für einen solchen Empfänger gibt es keinen Abgleich mit den Stammdaten, keine Standübernahme und keine Vertretungskopie; die Anschrift der Institution bleibt. Zusätzliche Empfänger bleiben wählbar, die frühere Sperre „keine Auswahl ohne Kontaktbezug“ entfällt. Zurück zu einem Stammdatenkontakt führt der vorhandene Empfängerwechsel; danach trägt der Empfänger den Zusatz nicht mehr. Eine Verfahrenskopie nimmt den Standardkontakt. Eine Beteiligung, deren Kontaktbezug schon vorher leer war und die nie eine solche Meldung hatte, gilt weiter als „Kontakt fehlt“. SPEC-RAL-QM-HP-6 bleibt unverändert; die Ausnahme steht hier und in SPEC-RAL-QM-HP-41, Fachregel 7.

ADR-15 · Organisationsanmeldung über Keycloak-Brokering mit portalgeführtem Ausfallweg

Status: angenommen durch Benutzerentscheidung vom 06.10.2026 zu RAL-QM-HP-63 (O1–O3). (ADR-14 ist RAL-QM-HP-41, ADR-16 ist RAL-QM-HP-50.)

Kontext: Interne Mitarbeitende sollen sich mit der Kennung eines vorgeschalteten Organisationsdienstes anmelden; fällt er aus, mit den bei Keycloak verwalteten Zugangsdaten. Das Portal kennt Benutzer nur über das Keycloak-Subject und darf keinen zweiten Aussteller und keine automatische Kontoübernahme bekommen.

Entscheidung: 1. Brokering, anbieterneutral: Keycloak bindet den Dienst als OIDC-Identity-Provider organisation an (feste Endpunkte, PKCE, Signatur über JWKS). Ein brokered Login endet im bestehenden Keycloak-Benutzer — dasselbe Subject wie beim Passwortweg, also dieselbe lokale ABP-Projektion. Nachweis gegen einen zweiten Keycloak-Realm als simulierten Dienst. 2. Kontrollierte Zuordnung: Der First-Broker-Login-Ablauf besteht nur aus „Deny Access“. Zugang hat allein ein Organisationskonto, dessen Kennung der Systemadministrator am Konto hinterlegt hat (Keycloak-Federated-Identity, gesetzt über den Provisionierungsclient). Das Portal verweigert eine Kennung, die bereits einem anderen Konto gehört — Keycloak selbst ließe sie doppelt zu (gemessen mit 26.7). 3. Ausfallweg im Portal: Ein Hintergrunddienst prüft das Discovery-Dokument (60 s, 3/3 Entprellung); ein globaler Schalter (eigenes Recht, Sicherheitsprotokoll) erzwingt den Ausfallweg. Beides ändert nur das Angebot der Anmeldeseite; die Passwortanmeldung bei Keycloak ist immer möglich und unverändert geprüft. Der Organisationsknopf öffnet dieselbe ABP-Anmeldung mit kc_idp_hint. 4. Nachweis des Wegs: Keycloak-Ereignisse (LOGIN, IDENTITY_PROVIDER_*) und der Claim identity_provider; das Portal schreibt je validierter Rückkehr einen Eintrag ins ABP-Sicherheitsprotokoll (Weg, Benutzer, Subject; Zeit und IP von ABP).

Konsequenzen: Keine neue Tabelle; die Zuordnung lebt in Keycloak. Weil Keycloak sie nicht eindeutig hält, laufen alle Zuordnungs-Schreibvorgänge unter einer SQL-Server-Anwendungssperre (sp_getapplock, transaktionsgebunden, instanzübergreifend) und danach unter der Kontoreservierung, die auch der Kennungswechsel nimmt; jede Änderung erhöht die Revision des Kontos (Review !220). Abschalten deaktiviert einen vorhandenen Provider, statt ihn liegen zu lassen. Alle internen Konten behalten ihr Einladungs-Passwort; ein Entzug im Organisationsverzeichnis wirkt nicht auf den Ausfallweg (Deaktivierung im Portal, kein Verzeichnisabgleich). Der Ausfallzustand ist prozesslokal; mehrere Instanzen prüfen jeweils selbst. Ein eingerichteter zweiter Faktor wird seit RAL-QM-HP-11 auch nach der Organisationsanmeldung abgefragt (ADR-19, Post-Broker-Login-Ablauf).

ADR-16 · Schadsoftwareprüfung zentral in CAF Documents mit ClamAV

Status: angenommen durch Benutzerentscheidung vom 05.10.2026 zu RAL-QM-HP-50 (O1–O3). (ADR-14 ist RAL-QM-HP-41, ADR-15 ist RAL-QM-HP-63.)

Kontext: Hochgeladene Dateien dürfen erst nach einer erfolgreichen Schadsoftwareprüfung ausgeliefert werden. Bytes verlassen das System über zwölf Produktwege, darunter Downloads, ZIP-Export, Mailanhang und Kopien, sowie über den Share-Link. Alle Produktwege laufen über DocumentContentOperations.OpenAsync in CAF Documents. Betrieb ist ein eigener Docker-Stack ohne Cloud-Dienst.

Entscheidung: 1. ClamAV (clamd) über INSTREAM auf TCP, ohne Zusatzpaket, als Container in jedem Stack. 2. Durchsetzung in CAF Documents: Der Prüfstand liegt als internes ExtraProperty am ABP-Deskriptor; gesperrt wird an beiden Lesepunkten, jeweils nach der Fachautorisierung des Aufrufers. Der dauerhafte Prüfauftrag ist eine CAF-eigene Tabelle CAFDocumentScanJobs neben ABP File Management, wie die Share-Grants; Metadaten und Inhalt werden nicht dupliziert. 3. Sofortige Prüfung beim Upload mit begrenzter Wartezeit. Bei Befall wird abgewiesen, ohne Ergebnis gespeichert und gesperrt, der Auftrag prüft nach. Ein fehlender Befund ist nie unbedenklich, auch nicht im Altbestand. 4. Was das Produkt selbst rendert, gilt als unbedenklich (DocumentContentOrigin.SystemGenerated, architekturgetestet auf drei Erzeuger); eine Kopie übernimmt den Befund der Quelle.

Konsequenzen: - Kein Abschalter: Ohne erreichbaren Scanner bleibt alles gesperrt. - Jeder Stack braucht den Dienst; der Development-Stack bekommt dafür den Port CLAMAV_PORT. Bestehende Stack-Beschreibungen werden beim nächsten Start nachgerüstet. - Signaturaktualisierung braucht ausgehenden Zugang zu database.clamav.net. - Die Produkte lesen den Prüfstand aus DocumentDescriptorDto.ScanState und zeigen ihn an. Eine gestörte Prüfung oder ein Befall erscheint über ein lokales Ereignis auf der Störungsseite.

ADR-17 · Rückläufer über ein IMAP-Postfach mit signierter Message-ID je Versandversuch

Status: angenommen durch Benutzerentscheidung vom 08.10.2026 zu RAL-QM-HP-76 (Eingangsweg, Zuordnung, Klärung, Testquelle).

Kontext: Unzustellbarkeitsmeldungen des Mailtransports sollen automatisch dem betroffenen Versandversuch zugeordnet werden. RAL hat Transport und Testquelle nicht benannt; das Portal versendet über ABPs IEmailSender (SMTP). Eine öffentliche, ungesicherte Rückmeldeschnittstelle und eine Zuordnung über die E-Mail-Adresse sind ausgeschlossen.

Entscheidung: 1. Eingang über ein eigenes Postfach per IMAP (MailKit), Abruf durch einen Hintergrunddienst; jede Nachricht in eigener Transaktion, Verschieben erst nach dem Commit. Ausgewertet werden RFC-3464-Meldungen; nur Action: failed zählt. 2. Signierte Message-ID je Versuch (<ralqm.{Zeile}.{Versuch}.{HMAC}@{Domain}>). Zuordnung nur bei genau einer Referenz mit gültiger Signatur; zustandslos, nichts wird je Mail gespeichert. 3. Klärungsliste für alles andere (Tabelle RALQMHearingDeliveryReports, eigenes Recht, kein Meldungstext, Erledigen mit Begründung, keine manuelle Zuordnung). 4. Die Zuordnung nutzt dieselbe Regel wie der interne Erfassungsweg aus RAL-QM-HP-24 (HearingBounceRecorder); Dubletten über die Meldungsreferenz.

Konsequenzen: Neues Paket MailKit (MimeKit) im Hearings-Runtime. Ohne Signierschlüssel bleibt der Versand unverändert und keine Meldung ist zuordenbar; das Abschalten des Postfachs lässt Meldungen beim Transport liegen. Den Schlüssel zu wechseln macht bereits versendete Referenzen unzuordenbar (sie landen in der Klärung). Ein Empfänger kann mit der Message-ID seiner eigenen Mail nur deren Zustellung als gescheitert melden. Development/Local nutzen GreenMail; die Abnahme mit dem realen RAL-Transport ist offen.

ADR-18 · Rundnachrichten als eigener Kontext der Outbox im Hearings-Feature

Status: angenommen durch Benutzerentscheidung vom 09.10.2026 zu RAL-QM-HP-77 (Vorschlag O7 aus RAL-QM-HP-24).

Kontext: Ein Referent soll eine Rundnachricht an alle Institutionen einer Gruppe senden, ohne dass es ein Verfahren gibt. Die Outbox verlangte bisher je Zeile eine HearingId; der Vorlagenkatalog kannte nur verfahrensbezogene Zwecke. Ein Dummy-Verfahren oder Guid.Empty ist ausgeschlossen, ebenso eine vorsorgliche Herauslösung eines CAF-Kommunikationsbausteins.

Entscheidung: 1. Eigenes Aggregat HearingCircular im Hearings-Feature (Tabelle RALQMHearingCirculars): Entwurf mit Gruppe (nur Id und Name), Wortlaut mit Platzhaltern und Anhängen; genau ein Versand, danach unveränderlich. Seine Id ist die Vorgangskennung. 2. Kontextart der Outbox: HearingId ist nullable, CircularId neu; ein Check-Constraint verlangt genau eines von beiden. Schlüssel circular:{Rundnachricht}:{Institution}, Batch = Rundnachricht. Versand, Wiederholung, Unzustellbarkeit (ADR-17) und Störungen laufen über dieselbe Outbox; der Verfahrensverlauf entfällt, weil es kein Verfahren gibt. 3. Eigener Vorlagenzweck Circular (90) im Katalog, nur als Nachricht, nur verfahrensfreie Platzhalter; er gehört zu keinem Vorlagensatz eines Verfahrens (HearingParticipantMessageKinds bleibt unverändert, HearingTemplatePurposes = Katalog). 4. Gruppen und Kontakte kommen über Institutions.Contracts; die Vertretung löst weiterhin nur der HearingOutboxManager auf. Anhänge laufen über den CAF-Dokumentport und gehen erst nach freigegebener Schadsoftwareprüfung hinaus.

Konsequenzen: Code, der Outbox-Zeilen liest, muss eine Zeile ohne Verfahren vertragen (Postausgang, Rückläufer-Verlauf, Störungen). Die Verfahrensseiten sehen Rundnachrichten nicht. Eine korrigierte Neuzustellung (RAL-QM-HP-35) gibt es für Rundnachrichten nicht; „Erneut senden“ im Postausgang wirkt wie bei jeder Zeile.

Referenzen

  • Laufendes ADR-Register mit Vor-/Nachteil je einzelner Konvention: docs/conventions-decisions.md
  • Gesamtarchitektur inkl. Modulverzeichnis und Abhängigkeitstabelle: Bausteinsicht; Qualitätsnetz: Querschnittliche Konzepte §7
  • Nachvollziehbarkeit der Entscheidungsumsetzung je Arbeitsschritt: Git-Historie und Merge Requests.
  • Vertiefung Bausteine/Schnitt: Bausteinsicht; Vorgaben für neue Features: Entwicklungsleitfaden, conventions/

ADR-19 · Zweiter Faktor über Keycloak-TOTP mit persistentem Rücksetzauftrag

Status: angenommen durch Benutzerentscheidung vom 13.10.2026 zu RAL-QM-HP-11 (O1–O3, Zusammenspiel mit RAL-QM-HP-63). (ADR-18 ist RAL-QM-HP-77.) Die begrenzte Sprachregel in Punkt 5 wurde durch person-001 am 19.10.2026 bestätigt.

Kontext: Interne und Institutionskonten sollen freiwillig einen zweiten Faktor einrichten und selbst entfernen können; bei Verlust setzt ihn ausschließlich ein Systemadministrator zurück. Keycloak ist alleinige Stelle für Passwörter, Faktor-Geheimnisse und Authentifizierung; das Portal darf kein Geheimnis, keinen Code und kein Passwort sehen.

Entscheidung: 1. TOTP über Keycloak-Aktionen (O1): Einrichten über die anwendungsinitiierte Aktion CONFIGURE_TOTP, Entfernen über delete_credential:{id} mit max_age=0 (frische Passworteingabe). Der Rundweg folgt dem Passwortwechsel aus RAL-QM-HP-13: Einmal-Token im OIDC-State, Ergebnis serverseitig im validierten Ticket. Ob sich etwas geändert hat, entscheidet danach der Credential-Bestand bei Keycloak, nicht der gemeldete Status. TOTP-Parameter (SHA1, 6 Stellen, 30 s) stehen ausdrücklich in Realm-Import und Abgleich; jede Authenticator-App genügt, ein Firmengerät ist nicht nötig. 2. Sitzungen bei Selbstbedienung (O3): Nach Einrichten oder Entfernen enden alle anderen Provider- und CAF-Sitzungen, der Security Stamp wird erneuert; die aktuelle Sitzung bleibt. 3. Persistenter Rücksetzauftrag (O2): Eigenes Recht RALQM.Accounts.ResetSecondFactor, nur Systemadministrator, nie das eigene Konto. Der Auftrag (RALQMSecondFactorResetOrders, Id = Operation-ID des Dialogs) bindet die beim Lesen gesehenen OTP-Credential-Ids und läuft in Schritten mit je eigener Unit of Work: Faktor entfernen → Provider-Sitzungen beenden, Stamp erneuern, Kontoverlauf (eine Einheit) → CAF-Sitzungen widerrufen → Benachrichtigung. Ein Teilfehler hinterlässt einen offenen, in der Kontenübersicht sichtbaren Auftrag; derselbe Aufruf setzt ihn fort, auch nach einem Neustart. Ein gefilterter Unique-Index lässt je Konto nur einen Auftrag mit offenen Sicherheitsschritten zu ([Step] < 30); ein Auftrag, der nur noch auf die Benachrichtigung wartet, sperrt den nächsten nicht. Der Concurrency-Stamp verhindert, dass ein paralleler Versuch einen Schritt doppelt schreibt; die Benachrichtigung wird vor dem Versand am Auftrag beansprucht (NoticeAttemptedAtUtc, zwei Minuten), sodass parallele Fortsetzungen sie einmal senden. Sie nennt den Rücksetzzeitpunkt, nicht den Versandzeitpunkt. Die Identitätsprüfung geschieht außerhalb des Portals und wird im Dialog bestätigt. 4. Organisationsanmeldung (HP-63): Der Organisations-IdP erhält den Post-Broker-Login-Ablauf ralqm-organisation-post-login-otp (bedingter Unterablauf: „Condition - user configured“ → „OTP Form“, danach ein Pflichtschritt „Allow access“). Wer einen Faktor eingerichtet hat, wird auf jedem Weg danach gefragt. Der Pflichtschritt ist nötig, weil Keycloak einen Ablauf ohne ausgeführten Schritt als gescheiterte Anmeldung wertet. 5. Sprache der Rücksetzbenachrichtigung (Benutzerentscheidung vom 19.10.2026): HP101/ADR-22 bleibt die einzige Quelle: internes Kontoprofil bzw. gebundener Institutionskontakt, mit der dortigen getrennten Eigenkonto-Sprachaktion. Für neue administrative Faktor-Resetaufträge wird die aktuell aktive Empfängerpräferenz festgeschrieben. Fehlende oder inaktive Präferenz bedeutet für diese Sicherheitsmail Deutsch, auch wenn HP101s globaler Standard anders lautet. Es entsteht keine zusätzliche Beschränkung auf zwei Kulturen: eine weitere aktive HP101-Kultur bleibt gültig. Erstbearbeiter, späterer Administrator, Worker, Präferenzwechsel und spätere Deaktivierung einer Sprache ändern den Snapshot nicht. Bestehende Aufträge ohne Snapshot verwenden Deutsch. Die Lokalisierung von Betreff/Text und die Darstellung des ursprünglichen UTC-Rücksetzzeitpunkts folgen diesem Snapshot und HP101s Formatter (E26); Empfängeradresse und Anzeigename werden weiterhin beim Versand aus dem Konto gelesen. Die Mail als Ganzes ist kein Snapshot. Alle übrigen Mail-/Kontakt-/Fallbackregeln aus HP101 und ADR-22 bleiben unverändert.

Konsequenzen: Eine neue Tabelle mit Migration. Fehlversuche beim Code zählen auf die bestehende Brute-Force-Sperre von Keycloak (AC-4), es gibt keinen Portalzähler. Ist Keycloak direkt nach dem Rundweg nicht lesbar, meldet das Portal „nicht bestätigt“, verbraucht den Vorgang nicht und bietet „Erneut prüfen“ an, solange das Einmal-Token gilt (15 Minuten); erst die Prüfung wendet die Sitzungsfolgen an. Recovery-Codes, E-Mail-OTP und ein zweiter Genehmiger sind nicht beauftragt.

ADR-20 · Website-Abgleich mit Quittung, Seitenbildung und Fristüberwachung

Status: angenommen durch Benutzerentscheidung vom 14.10.2026 zu RAL-QM-HP-78 (O1–O4).

Kontext: ADR-12 liefert den Bestand als Pull, sieht aber nicht, ob die Website ihn übernimmt. Der Betreiber steht weiter nicht fest. Der Abruf hatte weder Seitenbildung noch Obergrenze (Review !177, H9).

Entscheidung: 1. Pull mit Quittung (O1): Die Website ruft wie bisher ab. Danach meldet sie mit POST /api/gz/hearings/public/receipts je Kennung den übernommenen Änderungszeitpunkt zurück; eine Entfernung meldet sie mit removed. Es gelten derselbe Client, dieselbe Policy und dieselbe Aufrufbremse wie beim Abruf. Das Portal hält je Kennung den zuletzt bestätigten Stand fest (RALQMHearingPublicationConfirmations, Id = öffentliche Kennung). Der Stand bewegt sich nur vorwärts. Angenommen wird nur ein Stand, den der Abruf geliefert hat oder liefert: nicht neuer als der aktuelle und zum gleichen Zeitpunkt von gleicher Art. Zwei gleichzeitige Quittungen für eine Kennung: Eine gewinnt, die andere erhält 409 und sendet erneut. Jede Quittung, auch eine leere, ist eine Zeile in RALQMHearingPublicationReceipts; die jüngste ist der letzte erfolgreiche Abgleich. Ein Abruf allein zählt nicht. 2. Testgegenstelle (O2): Der Website-Client entsteht im Kundenrealm durch reconcile-website-client.sh, nur wenn KEYCLOAK_WEBSITE_CLIENT_SECRET gesetzt ist. Er ist ein reiner Maschinenclient ohne Rollen, mit der Audience ral-qualitymarks-publication-feed. Nachgewiesen wird mit einem echten Keycloak-Token. Die Abnahme mit dem realen Betreiber steht aus. 3. Frist (O3): Hearings:PublicationSync:StaleAfter, Vorgabe 60 Minuten. Der Abgleich ist überfällig, wenn eine öffentliche Änderung oder Entfernung länger unbestätigt bleibt, und stumm, wenn die Website so lange keine Quittung sendet. Der HearingPublicationSyncWorker prüft im Takt CheckInterval (fünf Minuten) und meldet das als Störung der Quelle PublicationSync, final, also mit einer Betriebsbenachrichtigung je Ausfall. Mit dem nächsten gesunden Lauf ist die Störung aufgelöst. Jeder Ausfall ist eine eigene Störung; ihre Id leitet sich aus der Zahl der früheren ab, damit zwei Instanzen dieselbe nennen. Offene, letzte und Anzahl stammen aus einer einzigen Abfrage (Review !262). Ein Rückfall innerhalb einer Frist nach der Auflösung öffnet dieselbe Störung wieder, ohne neue Benachrichtigung. Beide Fristen laufen frühestens ab dem Start der Anwendung mit eingeschaltetem Abruf. Ist der Abruf ausgeschaltet, wird nichts geprüft. 4. Seitenbildung (O4): Der Abruf liefert höchstens 500 Einträge je Seite (pageSize, Vorgabe und Obergrenze). Die Fortsetzung continuation setzt nach der letzten öffentlichen Kennung in der Sortierung der Datenbank fort (Keyset, nicht Offset). Alle Seiten eines Abrufs tragen den Cursor der ersten. IsComplete ist nur auf der letzten Seite eines vollständigen Abrufs wahr; erst dann entfernt die Website, was ihr fehlt. Eine Quittung trägt höchstens 500 Einträge. Ein vollständiger Abruf liefert zusätzlich die Entfernungen, deren Quittung noch aussteht, mit ihrer Änderungszeit (Review !262). Eine Website, die sich nach einer Unterbrechung mit einem vollständigen Abruf neu verbindet, kann sie so bestätigen. 5. Vertrag der Quittung: Die Website quittiert nach jedem Abruf, mindestens alle 15 Minuten, auch ohne Einträge, und gibt die Änderungszeit genau so zurück, wie sie geliefert wurde. Eine bestätigte, aber nicht mehr aktuelle Kennung nennt die Antwort unter outdated. Das gilt auch für Entfernungen: Eine verspätete Quittung einer früheren Entfernung schließt eine spätere nicht (Review !262). 6. Abgleich im Deploy (Review !262): Der Website-Client gehört zum Client-Scope. reconcile-client.sh bindet reconcile-website-client.sh selbst ein, damit auch der Deploy-Einstieg, der nur den Client-Scope ausführt, ihn anlegt.

Konsequenzen: - Zwei neue Tabellen (Migration AddHearingPublicationSync), eine neue Störungsquelle und ein neuer Worker. - Die Störungsseite zeigt den letzten erfolgreichen Abgleich und die offenen Bestätigungen. - Quittungen älter als ReceiptRetention (30 Tage) werden gelöscht, die jüngste bleibt. - Bisherige Aufrufer erhalten höchstens 500 Einträge und eine Fortsetzung; IsComplete gilt erst für die letzte Seite. - Die Website muss nach jedem Abruf quittieren, auch wenn sie nichts übernimmt; sonst gilt der Abgleich nach der Frist als stumm.

RAL-QM-HP-31: Feste Institutionsbindung ersetzt Gast und Claim (17.09.2026)

Entschieden durch Annahme der Spec: Authorization besitzt Zuordnung, Kontoeignung und Bindungswiderruf/-rücknahme; Hearings konsumiert nur den prozessinternen Reader und besitzt Teilnahme, Fristen und Grants. Mehrere Konten teilen die Beteiligungen ihrer Institution. Permission allein, E-Mail, Providergruppe, URL und historische Teilnahmebindung reichen nie. Jede Operation liest aktuelle Zustände. Schreibzugriff und Widerruf sind serialisiert; Bindungsaktionen nehmen vor dem Lesen eine eigene Zeilenschreibsperre und prüfen Stempel, Operation-ID und Akteur. Wiederherstellung verändert weder Kontosperre noch Teilnahmeentzug. Die vorhandenen Unique-Indizes und ExtraProperties genügen; keine Schemaänderung.

Gast-/Claim-Fachdienste, Kontexttransport und UI entfallen vollständig. Historische Zeilen bleiben erhalten. Alte Einstiege führen datenfrei zum Login, alte Downloads liefern 404. CAF-Bearer-Shares sind nur im HearingPortal-Host deaktiviert (O2); CAF bleibt unverändert. Bindungswiderruf lässt Konto-Selbstbedienung zu (O1). RAL-QM-HP-52-Sitzungswerte bleiben 15 Minuten Inaktivität und acht Stunden absolut. Die integrierte Kopfzeile verwendet den bestehenden OwnAccount-Vertrag; es entsteht kein zweites Profilformular. Browserabnahmen bleiben optional und ungeprüft, bis ein entsprechender Lauf angefordert und bestanden ist.

Ergänzung RAL-QM-HP-5 (18.09.2026): Neben Kontosperre und Bindungswiderruf (beide in Authorization) tritt der gezielte Zugangsentzug je Beteiligung als dritter, eigenständiger Mechanismus in Hearings (HearingParticipant.AccessRevokedAtUtc, AC-10). Er ändert weder Konto noch Bindung: Die Bindung bleibt bestehen, die Institution bleibt beteiligt, nur dieses eine Verfahren ist für alle Konten der Institution im Portal unerreichbar, und die Wiederfreigabe braucht weder eine neue Einladung noch eine Aussendung. Wie die anderen Zustände wird er bei jedem Portalaufruf gelesen, nie in der Sitzung gehalten (AC-9).

ADR-21 · Produktweiter Bestands- und Recovery-Schutz über Prozessverträge

Datum: 15.10.2026 · Status: entschieden, Umsetzung und aktuelle Nachweise in RAL-QM-HP-65.

Kontext: Der bestätigte Wiederanlaufschutz aus AC-14 muss Ablage- und Verfahrensdateien vor weiteren Schreib- oder Löschzugriffen sperren. Ein Restore verändert auch ausschließlich produktseitige Dokumentreferenzen. Ein Hook allein am CAF-Inhaltsport erreicht diesen Weg nicht; ein SaveChanges-Hook wäre für nicht transaktionale Datei-I/O zu spät. Die bisherige vollständig einseitige Runtime-Contracts-Kante aus RAL-QM-HP-16 genügt deshalb für diesen neuen Umfang nicht.

Entscheidung: Filing besitzt die zentrale Fristpolicy, Bestandsprüfung und externe Recoverygeneration. Hearings.Runtime konsumiert die schmalen prozesslokalen Verträge IFilingStorageMutationGate, die Löschbefundprüfung und IFilingRetentionExecutionPolicy aus Filing.Contracts. Diese Verträge erzeugen oder committen keine Unit of Work. Der Aufrufer tritt nach Beginn seiner vorhandenen transaktionalen ABP-Unit-of-Work ein; die externe Mutationslease bleibt bis zu deren Dispose nach Commit/Rollback erhalten. Ein Revisionsmarker gehört in dieselbe Transaktion. Verschachtelte Aufrufe teilen den gehaltenen Lease, jede eigene Transaktion zeichnet ihren Stand auf. CAF-Upload-/Ablage-/Share-Verträge werden nur in dieser Produktkomposition über native ABP-Interception eingebunden. CAF besitzt keine RAL-Policy.

Die zentrale Policyzeile serialisiert Änderungen und den tatsächlichen automatischen Vollzug. Der besitzende Fachbaustein behält Objektregeln, Reservierung, dauerhaften Auftrag und Historie. Systemursache wird ohne erfundenes Benutzerkonto gespeichert; manuelles Vorziehen ist eine zusätzliche menschliche Anforderung und ersetzt den ursprünglichen Auslöser nicht.

Grenzen: Kein Runtimezyklus, keine fremden Entities/Repositories/Tabellen, kein allgemeiner Usage-Provider und keine zweite Uploadpipeline. Filing.Contracts bleibt frei von Hearings- und CAF-Runtimeabhängigkeiten. Die bisherige Ablage-Verfahrensintegration und ihr Verwendungsnachweis bleiben Filing-orchestriert. Eine eigene Querschnittsplattform wird nicht eingeführt.

Betrieb: Recovery ist standardmäßig aus. Aktiviert ohne gültige externe Generation und eindeutigen produktiven Prüfer bleibt die Komposition geschlossen. Vor dem Datenbank-Restore rotiert der Betrieb die außerhalb des Backups geführte Generation nach Drain der Mutationen. Erst ein vollständiger unveränderter Prüflauf genau dieser Generation und eine ausdrückliche separat berechtigte Freigabe heben die Sperre auf. Eine Datenbankwiederherstellung ohne diesen Betriebsschritt wird damit nicht automatisch erkannt. Normale Mutationen nach Freigabe machen laufende Beobachtungen veraltet; sie erzwingen keine erneute Generationfreigabe nach jedem Upload.

Verworfen: Ein neuer generischer UoW-/Repositorywrapper, ein Host-SaveChanges-Mechanismus für alle Dateizugriffe und eine präventive neue Crosscutting-Projektfamilie. Die bestehende ABP-UoW und schmale bestehende Modulgrenzen tragen die benötigte Verantwortung.

Nachweise: Die Graph-Gegenfälle erlauben Hearings.Runtime → Filing.Contracts und verweigern Hearings.Runtime → Filing.Runtime. FilingStorageMutationGateTests prüfen reale ABP-UoW-Lebenszyklen; Datei-/SQL-/Gegenproben und die endgültige Abnahme werden in der Spec von RAL-QM-HP-65 geführt.

ADR-22 · Kommunikationssprache als Eigenschaft des Empfängers, aufgelöst je Postausgangszeile

Status: angenommen durch Benutzerentscheidung vom 14.10.2026 (person-004) zu RAL-QM-HP-101, fachliche Bestätigung durch person-001 im MR-Review.

Kontext: Verfahrensnachrichten gingen in der Sprache des Bearbeiters bzw. fest auf Deutsch hinaus; Katalogvorlagen kannten genau Deutsch und Englisch unter eigenen Kennungen. Institutionen mit nicht deutschsprachigen Ansprechpartnern sollen ihre Nachrichten in ihrer Sprache erhalten, ohne dass die Anwendung auf zwei Sprachen festgelegt bleibt oder Empfänger selbst etwas pflegen.

Entscheidung: 1. Sprachen aus der Sprachverwaltung. Aktive Sprachen und Standardsprache sind die der ABP Language Management (ILanguageProvider, Abp.Localization.DefaultLanguage); Hearings rechnet Kulturnamen nicht um. Katalogvorlagen tragen je aktiver Sprache einen Text, der Text der Standardsprache ist Pflicht. Die Altkennungen de/en stellt der Migrator einmalig und wiederholbar auf die Kulturnamen um und meldet strukturiert. Wortgleiche Kopien des Standardtexts unter weiteren Sprachen entfernt er in jeder Vorlage und meldet jede (E11). 2. Sprache am Kontakt, wirksam beim Einreihen. Die Kommunikationssprache steht am Institutionskontakt (Pflege mit RALQM.Institutions.Update); leer, unbekannt oder inaktiv heißt Standardsprache; eine gespeicherte, später deaktivierte Sprache bleibt stehen, blockiert kein Speichern und gilt bis zur Änderung als Standardsprache (E12). Jede Postausgangszeile friert die wirksame Sprache ihres Empfängers im Moment des Einreihens ein; die Vertretungskopie ist identisch. Spätere Änderungen erreichen nur spätere Zeilen. 3. Auflösung im Renderer, nicht im Sendeweg. Kein Sendeweg nennt eine Sprache. Der Renderer kennt den geladenen Beteiligten und den gewählten Zusatzempfänger und löst die Sprache dort auf, wo er auch Anrede und Stand einfriert; der Postausgangsmanager schreibt die gemeldete Sprache auf die Zeile. Ein Renderer, der für eine solche Anforderung keine Sprache nennt, ist ein Programmierfehler, kein stiller Standard. Interne Mails gehen je Empfänger in dessen Kommunikationssprache hinaus (ergänzt am 16.10.2026, E26, E27). 4. Sprachstände je Zweck und Sprache. Der Kommunikationsschritt ist eindeutig je Verfahren, Zweck und Sprache; bearbeitet wird wie in der Textvorlagenverwaltung – „Anpassen pro Sprache“ öffnet die Bezugssprache lesend neben der bearbeitbaren Zielsprache –, nur in den Sprachen der Empfänger. Ein Speichern sperrt andere Sprachen nicht, es weist auf sie hin. Der Ausgabestempel umfasst alle Sprachstände. Nach der Browserprüfung (Entscheidungen vom 16.10.2026, E15, E16, E19–E21, Vereinfachung vom selben Tag): die Empfängerliste nennt alle Institutionen mit ihrer Sprache, Abweichung und Endfassung folgen der Sprache der gewählten Institution, die Zielsprachenwahl nennt die Empfänger je Sprache, Bezugs- und Zielsprache sind nie dieselbe, Nachfrage vor dem Verwerfen beim Wechsel von Sprache, Institution oder Zweck, ein gesperrter Sprachstand vergleicht nur mit deaktivierten Feldern; englische Datumswerte sind ausgeschrieben (E22). 5. Rückfall statt Abweisung, aber gekennzeichnet. Fehlt eine Sprachfassung im gebundenen Satz oder als Sprachstand, antwortet der aktuelle Text der Standardsprache; Nachsehen friert nichts ein, ohne eigenen Text spiegelt die Sprache den Standardtext, erst ein eigener Text macht den Sprachstand eigenständig (E13). Der Rückfall steht am Sprachstand (IsFallback, SourceCultureName) und an der Zeile (ContentCultureName) und wird in Vorschau, Zustellstatus und Postausgang gezeigt. Eine weitere Stufe gibt es nicht. 6. Rohdaten im Modell, Format im Renderer. Datumswerte reisen als yyyy-MM-dd, der Renderer schreibt das Kurzdatum der Zeilensprache; der Antwortabsatz der Bescheidung wird aus Erwiderung und Votum in der Zeilensprache zusammengesetzt. Freie Texte (Begründungen, Erwiderungen) werden nicht übersetzt. Ressourcentexte der Nachrichten und des Begleitschreibens kommen je Zeilensprache; der Dateiname des Begleitschreibens bleibt als technischer Bezeichner deutsch (E14).

Konsequenzen: Eine Migration (AddCommunicationLanguage: CommunicationLanguage am Kontakt; IsFallback und eindeutiger Index der Sprachstände; ContentCultureName an der Postausgangszeile, CultureName am Auftrag der Eingangsbestätigung). Die Eingangsbestätigung friert die Sprache beim Speichern ihres Auftrags ein. Das Modell einer Welle ist für alle Empfänger dasselbe; nur der Text je Zeile unterscheidet sich.

Ergänzung vom 16.10.2026 (person-004, Auftrag vom 12.10.2026): 7. Konten folgen dem Kontakt, der Anmeldedienst wird zuerst gefragt. Ein Empfänger mit Portalkonto ändert die Sprache seines eigenen Kontakts am eigenen Konto; Mitarbeitende tragen eine eigene Sprache am Konto (Anlage optional, Verwaltung und Selbstbedienung). Jede Sprachänderung setzt zuerst das locale-Attribut des Anmeldedienstes; nimmt er sie nicht an, wird die ganze Änderung abgewiesen und nichts gespeichert (E23). Institutions meldet geänderte Kontaktsprachen vor dem Speichern über einen Synchronisationsvertrag, den Authorization implementiert; die Selbstbedienung schreibt den gebundenen Kontakt über einen Schreibvertrag von Institutions. Kontomails des Anmeldedienstes und der Anwendung folgen der Kontosprache; der Kontoverlauf hält jede Änderung fest (E24). 8. Interne Mails je Empfänger; Rundnachricht je Sprache. Interne Mails nennen die Sprache jedes Empfängers, Zeitpunkte wie in den Empfängernachrichten (E26); Berichtsdokumente bleiben in der Standardsprache (E27). Die Rundnachricht trägt einen Text je Empfängersprache (Standardsprache Pflicht, weitere ganz oder gar nicht, E34) und geht je Institution in der Sprache ihres Standardkontakts hinaus, ohne eigenen Text mit dem gekennzeichneten Standardtext (E28, E29). Die Story-Migration wurde dafür neu erzeugt (E31).

ADR-23 · RAL-Keycloak-Kundentheme mit anwendungseigener Erweiterung

Datum: 19.10.2026 · Status: entschieden auf Benutzerauftrag zu RAL-QM-HP-102, D1–D3 bestätigt; Implementierung und aktuelle Abnahme werden in der Spec nachgewiesen. ADR-22 beschreibt die inzwischen integrierte Kommunikationssprache RAL-QM-HP-101.

Kontext: Die RAL-Anmeldung soll das vorhandene Kundendesign aufnehmen. Der HearingPortal-Client verwendet bereits das app-eigene ralqm-legal mit Rechtsfooter und Korrekturen der nativen Label-/Control-Zuordnung. Die reale Abnahme zeigt: Ein abgelaufener nativer Einladungslink verliert vor der Tokenvalidierung seinen Clientkontext und damit ein ausschließlich am Client gewähltes Theme. Das kundenweite Basistheme muss deshalb auch den Realm-Rückfall abdecken; app-eigene Rechtsziele dürfen ohne Clientkontext nicht geraten werden. TOTP und Kontenaktionen sind vorhandene Keycloak-Funktionen und bleiben beim Provider.

Entscheidung:

  1. Das kundenweite Basistheme ral-v1 liegt unter applications/RAL/etc/docker/shared/keycloak/themes/ral-v1 und erbt keycloak.v2. Das bestehende RAL-/HearingPortal-Produktdesign liefert Farb- und Layoutreferenz. Die Themequellen setzen diese in lokalem CSS um. Für dieses Theme stehen im Repository keine freigegebenen RAL-Logo- oder DIN-Pro-Dateien bereit; verwendet werden Systemschrift (system-ui, Segoe UI, Sans-Serif) und die native Realmbezeichnung. Neue Logo-/Fontdateien werden damit nicht als geliefert behauptet. Es gibt keine neuen Font-/CDN-/Trackinganfragen und keine Abhängigkeit auf CAF- oder HearingPortal-Laufzeitprojekte.
  2. Das app-eigene ralqm-legal erbt ral-v1 und behält Rechtsfooter, DE/EN-Meldungen sowie die bestehenden Korrekturen in template.ftl und login-config-totp.ftl. Rechtsziele entstehen weiterhin ausschließlich aus der Client-BaseUrl, mit no-referrer. Formaktionen, Schutzparameter, native Fehler und vorhandene TOTP-Abläufe bleiben erhalten.
  3. Die erste Aktivierung betrifft den bestehenden RAL-Realm ral. Sein Standard ist ral-v1, der HearingPortal-Client behält den Override ralqm-legal. Clientlose Ablauf-/Fehlerseiten und Clients ohne eigenen Override erhalten dadurch nur die Kundenbasis, ohne HP-Rechtsfooter oder erfundene Rückkehradresse. Dies präzisiert D2 nach dem realen Ablaufbefund vom 19.10.2026; native Mail-Links bleiben unverändert. Der geteilte Reconciler wählt ral-v1 ausschließlich für ral; andere Realms einschließlich ral-logolicense behalten keycloak.v2. Admin-/Account-Console, fremde Organisations-IdPs, Mailthemes und fachliche Authentisierungsregeln werden nicht umgebaut. Deren Anmeldung innerhalb ral erbt gegebenenfalls die Kundenbasis.
  4. Hell/Dunkel folgt der Betriebssystempräferenz mit Light als neutralem Default. Es gibt keinen neuen Präferenzspeicher und keine Übertragung des Portal-localStorage über Origins.
  5. Beide Themeverzeichnisse werden gemeinsam über die bestehenden Container-/Fixture-/CI-Wege ausgeliefert und gegen das gepinnte Keycloak-Image geprüft. Der Rückfall stellt die vorherige versionierte ralqm-legal-Fassung mit direktem parent=keycloak.v2 wieder her. Rechtsfooter und bestehende A11y-Korrekturen bleiben dabei erhalten. Zusätzlich wird nur der Realm-Default von ral auf den vorherigen Wert keycloak.v2 zurückgestellt; der Clientoverride bleibt erhalten. Der frühere Reconciler-/Importstand gehört deshalb zum Rückfall. Ein Kunden-Deployment ist nicht Bestandteil dieses Auftrags.

Konsequenzen: Die gemeinsame Kundenmarke und die app-eigene Rechtsnavigation behalten getrennte Ownership. Die zusätzliche Parentstufe verlangt einen realen Nachweis, dass Styles, lokale Assets, DE/EN-Meldungen und die vorhandenen App-Overrides zusammen auf allen erreichbaren Providerseiten funktionieren. Bei Keycloak-Upgrades werden die gepinnten App-Templates und die Themevererbung abgeglichen. Keine neue UI-Fassade, Authentifizierungsplattform oder Laufzeitabhängigkeit entsteht. Die Entscheidung bestätigt weder erfüllte ACs noch einen erfolgreichen Rollout; Nachweise bleiben in der Spec von RAL-QM-HP-102.

10. Qualitätsanforderungen

Dieses Kapitel benennt, welche Qualitätsmerkmale für das RAL Gütezeichen Anhörungsportal verbindlich sind, und macht sie an konkreten, prüfbaren Szenarien fest. Architekturentscheidungen in Bausteinsicht und Querschnittliche Konzepte werden gegen genau diese Anforderungen getroffen; Risiken und technische Schulden benennt, wo sie aktuell nur teilweise erfüllt sind.

10.1 Qualitätsbaum

Der ausschlaggebende Werttreiber ist ein rechtssicherer, nachvollziehbarer Anhörungsprozess auf einer Plattform, die als modularer Monolith langfristig ohne Umbau der Kernarchitektur weiterentwickelbar bleibt (vgl. Einführung und Ziele). Daraus leiten sich fünf gleichrangige Qualitätsäste ab, die im Projekt jeweils durch Architektur und durchsetzende Tests abgesichert sind statt nur dokumentiert zu werden (CLAUDE.md):

text
Qualität RAL-Gütezeichen-Anhörungsportal
├─ Wartbarkeit
│  ├─ No-Layer-Modulgrenzen (Contracts vs. Runtime vs. Blazor)
│  ├─ Compile-Time durchgesetzte Abhängigkeitsrichtung
│  └─ Proportionalität (keine vorsorglichen Abstraktionen ohne Consumer)
├─ Sicherheit
│  ├─ OIDC-Authentifizierung über Keycloak, lokale ABP-Autorisierung
│  ├─ Objektberechtigung je Zugriffsweg (intern, Institutionskonto, Direktfreigabe; übergangsweise Gast)
│  └─ Least-Privilege-Infrastruktur (DB-Principal, Keycloak-Provisioning-Client)
├─ Barrierefreiheit
│  ├─ WCAG 2.1 AA als Mindestziel
│  ├─ Cohesion als einzige Fach-UI-Bibliothek
│  └─ automatisierte axe-Prüfung im BrowserGate
├─ Testbarkeit und Verlässlichkeit
│  ├─ Nachweismatrix je Änderungsart (Unit/Integration/SQL/Browser)
│  ├─ Quality Gate je Push (Git-Hooks)
│  └─ zuverlässige Nebenwirkungen (Outbox, Idempotenz, Retry)
└─ Datenerhalt
   ├─ Data-preserving Migrationen bei Renames
   ├─ optimistische Concurrency auf mutablen Aggregaten
   └─ kein Hard Delete fachlicher Prozessdaten

10.2 Qualitätsszenarien

Jedes Szenario folgt dem Muster Stimulus → Reaktion → Nachweis. Die ID-Präfixe entsprechen den Ästen aus 10.1.

Wartbarkeit (WB)

ID Szenario Erwartete Reaktion Nachweis
WB-01 Ein Entwickler ergänzt in CAF.Applications.RAL.QualityMarks.HearingPortal.Hearings einen ProjectReference auf die Institutions-Runtime statt auf Institutions.Contracts. Der Build schlägt architektonisch fehl; die verbotene Kante GZ-Runtime → fremde GZ-Runtime ist untersagt (siehe Bausteinsicht §5.2.6). Architekturtests auf Projekt-/Paketkanten, Teil des Quality Gate
WB-02 Ein Framework-Baustein (CAF.Modules.*) referenziert versehentlich einen CAF.Applications.RAL.QualityMarks.HearingPortal.*-Namespace oder ein GZ-Fachwort. Referenz wird als Verstoß gegen die Framework-Neutralität erkannt und blockiert. Architekturtests (Namespace-/Kantenscan), Vertragsdatei VERTRAG.md je Baustein
WB-03 Ein neues Feld wird an Hearing ergänzt, obwohl UI/Consumer es nicht benötigen. Contract-DTO bleibt unverändert; Änderung erfolgt zuerst am Aggregate mit benannter Operation (DEVELOPMENT-GUIDE.md §14). Code-Review anhand der Nachweismatrix, EF-Mapping- und Migrationstest
WB-04 Ein neues Modul wird mit dem Namen Common oder Utils angelegt. Ablehnung im Review; Ownership-Frage aus dem Entscheidungsbaum ist nicht beantwortet. Konvention in DEVELOPMENT-GUIDE.md §2
WB-05 Ein Paket-/API-Element wird als „nicht mehr benötigt“ entfernt. Entfernung gilt erst nach Restore-/Publish-/Endpoint-Scan als vollständig; reine Quelltextlöschung genügt nicht. Nachweismatrix „Paket-/API-Entfernung“ (DEVELOPMENT-GUIDE.md §15.1)

Sicherheit (SI)

ID Szenario Erwartete Reaktion Nachweis
SI-01 Ein Benutzer ruft eine geschützte Anhörungsseite ohne gültige Keycloak-Session auf. OIDC Authorization Code mit PKCE erzwingt Anmeldung; ohne gültige Session kein Zugriff auf die Server-UI. Authentifizierungstests in CAF.Modules.Authentication, BrowserGate-Login/Abmeldung
SI-02 Ein angemeldeter Referent versucht eine Aktion, für die seine Rolle keine Permission besitzt (z. B. Verfahrensabbruch, den nur Administrator/Geschäftsführung dürfen). Application Service lehnt serverseitig ab; ein ausgeblendeter Button gilt explizit nicht als Autorisierung. Permission-Definition-/-Vergabe-/-Ablehnungstests, Architekturtest „jeder Endpunkt prüft Permission“
SI-03 Ein angemeldeter Nutzer eines Institutionskontos versucht, ein Dokument eines anderen Teilnehmers über eine erratene DocumentId abzurufen. Zugriff wird verweigert; jeder Aufruf revalidiert feste Institutionsbindung, Hearing-Status, aktiven Teilnehmer und konkreten Dokumentgrant neu – eine ID/URL allein verleiht nie Zugriff (siehe Laufzeitsicht §6.4). Autorisierungsmatrix-Tests (positiv/negativ), Leakage-Scan, HTTP-Tests gegen den realen Hostpfad
SI-04 Zwei gleichzeitige Einlösungen derselben Einladung (Konto-Übernahme; übergangsweise Gasteinladung) treffen in getrennten Transaktionen ein. Jede Transaktion prüft Einladung und Objektzustand erneut; nur optimistische Schreibkonflikte werden begrenzt wiederholt, ein zwischenzeitlicher Widerruf bleibt wirksam. Concurrency-/Parallelsitzungstests, BrowserGate-Parallelfall
SI-09 Ein Benutzerkonto einer Institution ruft Meine Teilnahmen ab, während dieselbe Institution weitere Benutzerkonten besitzt. Jedes Konto sieht ausschließlich die eigene Institution und die eigenen Teilnahmen (US-22c); ein Entzug des Kontos wirkt sofort und ohne Rückfallweg (US-22). Autorisierungsmatrix-/Portaltests je Konto, Browserpfad Institutionsportal
SI-05 Die Anwendungsdatenbank wird kompromittiert oder ein Entwicklungsrechner verbindet sich versehentlich mit dem SA-Zugang. Der Host verwendet im Regelbetrieb ausschließlich den dedizierten, nicht privilegierten ralqm-Principal statt eines SA-/Admin-Logins; SA- und Keycloak-Admin-Zugang bleiben beim Setup/Reconciler (Verteilungssicht §3, HearingPortal-Betrieb, Getting Started §6). doctor.cmd-Kategorienprüfung, Migrations-Fast-Path-Principal-Check
SI-06 Die kontrollierte Kontoprovisionierung ruft Keycloak für eine mehrdeutige Identität auf. Der Admin-Port scheitert geschlossen (fail-closed) und vergibt keine Fachrolle automatisch; der Keycloak-Provisioning-Client besitzt ausschließlich die Rolle manage-users, keine weitere Realm-Administration. Adapter-, Mehrdeutigkeits- und Berechtigungstests, Test-KeycloakProvisioningClient.ps1
SI-07 Ein Client verbindet sich außerhalb der Entwicklungsumgebung unverschlüsselt. HSTS und HTTPS-Redirect laufen vor Routing/Authentifizierung; Health-Endpunkte sind bewusst nicht ausgenommen. Quelltext-Sentinel TransportSecurityPipelineTests
SI-08 Ein direkter CAF-Dokument-Share-Link wird mehrfach erraten oder nach Ablauf verwendet. Secret ist nur gehasht gespeichert, rate-limitiert, läuft ab und ist einzeln widerrufbar. Sicherheits-/Delegations-/HTTP-Tests von CAF Documents

Barrierefreiheit (BF)

ID Szenario Erwartete Reaktion Nachweis
BF-01 Eine Fachseite (z. B. Verfahrensliste, Freigabe, Postausgang) wird gegen WCAG-2.1-AA-Kriterien geprüft. Keine serious/critical axe-Verstöße (Kontrast, Alternativtexte, Rollen) außerhalb einer versionierten, begründeten Ausnahmeliste. axe-Läufe im BrowserGate über alle Fach- und Portalseiten, tests/.../BrowserGate/a11y-ausnahmen.json
BF-02 Eine Tabelle mit sortierbaren Spalten (Institutionenliste, Anhörungsliste) wird ausschließlich per Tastatur bedient. Spaltenköpfe sind fokussierbar, aria-sort wird gesetzt und per Screenreader angesagt. Tastatur-/Fokus-Komponententests (bUnit), BrowserGate-Tastaturpfad
BF-03 Ein Statuswert (z. B. Freigabe abgelehnt, Frist überschritten) wird dargestellt. Status ist nicht ausschließlich farblich codiert, sondern zusätzlich textuell/symbolisch erkennbar. UI-Review anhand Querschnittliche Konzepte §5, bUnit-Rendertests
BF-04 Eine datengebundene Seite befindet sich im Zustand „Laden“, „Leer“, „Fehler“, „Verboten“, „Validierung“, „Speichern“ oder „veralteter Concurrency-Stand“. Jeder dieser Zustände ist sichtbar und bedienbar abgebildet, kein stiller Fehlzustand. bUnit-Zustandstests je Seite, BrowserGate-Fehler-/Retry-Pfade
BF-05 Fachcode versucht, eine MudBlazor-, Syncfusion- oder Blazorise-Komponente direkt zu rendern statt Cohesion zu verwenden. Verstoß wird erkannt; ausgenommen sind allein die MVC-/Account/**-Seiten. Blazor-seitig gibt es keine Ausnahme und kein Vendor-UI-Paket mehr. Architektur-/Quelltextprüfung auf UI-Bibliotheksnutzung
BF-06 Ein Besucher öffnet die Erklärung zur Barrierefreiheit ohne Anmeldung oder aus dem Portal. Dieselbe anonyme Route zeigt tatsächlichen Konformitätsstand, datierte bekannte Barrieren, Zeitplan und konfigurierten E-Mail-Rückmeldeweg. Fehlende Produktionsangaben verhindern den Start. RAL-QM-HP-79; Authentication-Options-/Renderingtests und BrowserGate customer-accessibility einschließlich Portal-Rechtsseiten

Testbarkeit und Verlässlichkeit (TV)

ID Szenario Erwartete Reaktion Nachweis
TV-01 Ein Entwickler pusht einen Arbeitszweig. Der pre-push-Hook bestimmt betroffene Apps über alle Ref-Diffs und den alten/neuen Projektgraphen. Serverseitige Tests und übrige Pflichtschritte laufen; alle vier Browsergruppen sind standardmäßig aus. Reine Doku prüft Reports. Rote oder fehlende angeforderte Schritte sowie ein falscher Tipp blockieren den Push. tools/git-hooks, Invoke-QualityGate.ps1, Git-Note refs/notes/quality-gate
TV-02 Ein Pflichttest wird versehentlich übersprungen (Skip-Attribut) statt zu bestehen. Das gilt als Fehlschlag des Gates; es gibt keine Ausnahmeliste. QualityGateAutomationTests (Skip-Attribut-Verbot in Testquellen)
TV-03 Ein sichtbarer End-to-End-Prozess (Login, Freigabe, Portalzugang) wird geändert. Vor Übergabe/Merge laufen die Pflichtschritte des lokalen Gates. Browserstrecke, axe, Tastatur/Fokus und Browser-Berechtigungsprüfungen bleiben optional; nur angeforderte Gruppen müssen bestehen. Der Clean Room baut BrowserGate, startet es aber nicht. Serverseitige Berechtigungstests bleiben Pflicht. tools/scripts/Invoke-QualityGate.ps1 -App ral/qualitymarks/hearingportal; optionale Schalter im Entwicklungsleitfaden
TV-04 Der Postausgang versucht, eine E-Mail zuzustellen, und der SMTP-Provider ist vorübergehend nicht erreichbar oder antwortet nicht. Der Sendeversuch wird über die persistente Hearing-Outbox erneut versucht. Lokal entstehen dabei keine Duplikate: Der eindeutige Idempotency-Key verhindert einen zweiten Auftrag, derselbe Versuch wird nie parallel ein zweites Mal gesendet, und ein verspätetes Ergebnis überschreibt keinen neueren Versuch. Über die SMTP-Grenze gilt mindestens einmal (at-least-once): Bricht der Prozess nach der Annahme durch den Provider vor der lokalen Speicherung ab oder wird ein tatsächlich angenommener Versuch nach dem Zeitlimit aufgegeben, kann die Nachricht ein zweites Mal zugestellt werden. Diese Grenze ist angenommen (person-001, 11.10.2026; Risiken). Retry-/Idempotenz-/Restart-Tests (HearingOutboxDispatchTests, für die Eingangsbestätigung HearingResponseReceiptSqlServerTests), Kennzahlen „Sendeversuche nach Fehlercode“
TV-05 Eine EF-Migration wird gegen eine leere Datenbank ausgeführt und anschließend dotnet ef migrations has-pending-model-changes geprüft. Migration läuft vollständig „from-empty“ durch; das Modell hat keine ausstehenden Änderungen. SQL-Server-Integrationstests, Pending-Model-Check im Gate
TV-06 Eine neue Fachregel/Statusübergang wird implementiert. Positive und negative Tests decken alle relevanten Statuszellen ab, nicht nur den Erfolgspfad. Nachweismatrix „Domainregel/Transition“ (DEVELOPMENT-GUIDE.md §15.1)

Datenerhalt (DE)

ID Szenario Erwartete Reaktion Nachweis
DE-01 Ein Rename benannter Identifikatoren (z. B. das RALQM-Präfix-Rework) wird auf eine Datenbank mit vorhandenen Anhörungs- und Institutionsdaten angewendet. Die Migration ist data-preserving: bestehende Zeilen, Foreign Keys und Werte bleiben erhalten, nur Bezeichner ändern sich; historische EF-Migrationen behalten ihre alten Identifiers. HearingPortalDbMigrationServiceTests, SQL-Server-Round-Trip-Tests, DocumentsMigrationBaselineSqlServerTests
DE-02 Zwei Benutzer bearbeiten dieselbe Anhörung gleichzeitig und speichern nacheinander. Optimistische Concurrency verhindert einen stillen Teil-Overwrite; der zweite Schreibversuch scheitert nachvollziehbar am veralteten Stand. Concurrency-Tests gegen den realen Persistenzpfad (DEVELOPMENT-GUIDE.md §15.1)
DE-03 Eine Institution soll gelöscht werden, obwohl sie noch in Teilnehmer-Snapshots oder Portal-Kontobindungen referenziert wird. Löschung wird über den Usage-Provider verweigert; die UI erklärt den Deaktivieren-Hinweis statt eines harten Fehlers. Kontakt-/Institutions-Löschguard-Tests
DE-04 Ein laufendes Verfahren wird abgebrochen statt hart gelöscht. Der Zustand wechselt in einen benannten Endzustand (Aborted), Einladungen/Grants/Due Actions werden widerrufen, die fachliche Historie bleibt lesbar. Fach-, Berechtigungs- und Historientests „Verfahrensabbruch“
DE-05 Zeitstempel werden über verschiedene Server-Instanzen hinweg verglichen (z. B. Fristberechnung). Alle Zeitpunkte werden konsistent in UTC über ABP-Clock-Abstraktionen verarbeitet, keine lokale Serverzeit. Zeitgrenzen-/Fristvorschlagstests

10.3 Nicht angestrebte Qualitätsmerkmale

Entsprechend den bewusst nicht vorhandenen Nachbarn in Kontextabgrenzung §3.4 werden horizontale Skalierung über Microservices, eine zweite Datenbank, generische Workflow-/DMS-Fähigkeiten und Multi-Tenancy bewusst nicht als Qualitätsziele verfolgt. Das schont Komplexitätsbudget zugunsten der oben genannten fünf Äste; ein neuer Bedarf verlangt zuerst einen konkreten, getesteten Anwendungsfall, keine vorsorgliche Infrastruktur.

Siehe auch

  • Bausteinsicht – wo die Modulgrenzen aus 10.1 technisch verankert sind
  • Querschnittliche Konzepte – Sicherheits- und UI-Mechanismen im Detail
  • Risiken und technische Schulden – aktuell noch offene Nachweise (z. B. integrierter BrowserGate-Lauf)
  • DEVELOPMENT-GUIDE.md §15 – vollständige Nachweismatrix und Gate-Mechanik
  • GETTING-STARTED.md – lokale Ausführung von Gate und BrowserGate
  • conventions/security.md, conventions/testing.md – repo-weite Konventionsbaseline
  • Risiken und technische Schulden – Status und Nachweistiefe je offener Fachfunktion

11. Risiken und technische Schulden

Dieses Kapitel listet bekannte Risiken und technische Schulden des aktuellen Standes. Es ersetzt keine laufende Nachverfolgung; verbindliche Quelle für den jeweils aktuellen Stand bleiben Git-Historie und Merge Requests. Den Plan des Monorepo-Umbaus samt der damals offenen Vor-Push-Schritte führt docs/history/VOR-ERSTEM-PUSH.md — abgeschlossene Umbau-Historie, kein gültiger Statusbericht.

Übergreifender Kontext: Monorepo-Umbau vollzogen, Repository angebunden

Der Umbau zum eigenständigen Repository caf ist durchgeführt: Dieses Repository trägt eine neue Historie ohne den alten Verlauf (ein Initialcommit chore: initialize CAF monorepo) und besitzt die Branches main und development.

Die Remote-Anbindung ist hergestellt: origin zeigt auf https://gitlab.com/development174835/caf, main und development sind gepusht, und die CI-Pipeline läuft (.gitlab-ci.yml mit den Stages test, secret-detection, app-build, app-image, deploy). Änderungen erreichen main über Merge Requests; jeder Release trägt entsprechend einen Merge-Commit an der Spitze (05cccb5 Merge branch 'development' into 'main'). Nicht nachweisbar ist aus dem Repository heraus, ob GitLab diesen Weg serverseitig erzwingt — dazu die Zeile „Serverseitiger main-Schutz nicht nachgewiesen" unten.

Mehrere der unten aufgeführten Risiken sind Folgen dieses Umbaus (RALQM-Umbenennung, offene Nachweise). Den damaligen Erledigt-/Offen-Stand führt docs/history/VOR-ERSTEM-PUSH.md; dieses Dokument liegt zusammen mit dem ursprünglichen Übergabeprotokoll des Quellrepositorys als abgeschlossene Umbau-Historie unter docs/history/.

Risiko- und Schuldenübersicht

Thema Beschreibung Auswirkung Gegenmaßnahme
RAL-QM-HP-52 Stufe B Institutionsbindung, Kontomutations-Widerruf, Einladungen und externe Profile benötigen RAL-QM-HP-18. Stand 2026-09-18: Stufe A und B1 sind geliefert; offen bleiben B2–B4 (Spec-Status „Teilweise implementiert"). Die frühere Formulierung „Stufe A ist kein Abschluss von RAL-QM-HP-52" unterschätzt den Fortschritt. Offene Kriterien und Cohesion-Upstream UP-708-01 in der Spec weiterführen.
Prozesslokale Sitzungen Hostneustart verwirft aktive Sitzungen; mehrere Instanzen teilen keinen Zustand. Neue Anmeldung nach Neustart; kein Scale-out-Vertrag. Einzelhostbetrieb; verteilten Betrieb erst nach eigener Entscheidung und Tests freigeben.
Kontenermittlung über das Zeitverhalten der Anmeldung (RAL-QM-HP-91 E2) Anmeldeseite und Passwortprüfung liegen in Keycloak; die Anwendung sieht keinen Fehlversuch. Die Meldung ist für unbekannte Kennung, falsches Passwort und deaktiviertes Konto gleich (RAL-QM-HP-52 AC-5). Ob die Antwortzeiten sich unterscheiden, ist nicht gemessen. Ein Angreifer könnte über Antwortzeiten auf vorhandene Kennungen schließen. Das Restrisiko liegt bei Keycloak; im Anwendungscode ist es nicht behebbar. Angenommen, Entscheidung von person-001 am 11.10.2026 (RAL-QM-HP-91, AC-5 entfallen). Brute-Force-Schutz des Realms aktiv (ral-realm.json: failureFactor 10, quickLoginCheckMilliSeconds 1000, minimumQuickLoginWaitSeconds 60, maxFailureWaitSeconds 900, keine Dauersperre). Keine Messung in der Anwendung: Sie wäre hardwareabhängig und instabil. Bei Keycloak-Updates die Herstellerhinweise zu Enumeration und Zeitverhalten prüfen.
Doppelzustellung über die SMTP-Grenze (at-least-once) Der Postausgang (Hearing-Outbox) und die Benachrichtigungsaufträge der Eingangsbestätigung (RAL-QM-HP-35) speichern den Versanderfolg erst nach der Annahme durch den SMTP-Provider. Zwei Fenster führen zu einem zweiten Versand derselben Nachricht: (1) Der Prozess endet nach der SMTP-Annahme, aber vor der lokalen Speicherung des Erfolgs; nach dem Wiederanlauf wird der Auftrag als unterbrochen übernommen und erneut gesendet. (2) Ein Versand antwortet weder innerhalb der Wartezeit T (StuckAttemptTimeout) noch einer weiteren Beobachtungsfrist; der Versuch wird aufgegeben und neu gesendet, obwohl der Provider die erste Nachricht tatsächlich angenommen hatte. Lokal entstehen keine Duplikate: ein eindeutiger Auftrag je Nachricht, kein paralleler Zweitversand desselben Versuchs, und verspätete Ergebnisse überschreiben keinen neueren Versuch. Ein Empfänger kann eine Eingangsbestätigung, einen Hinweis an den zuständigen Referenten oder eine Nachricht des Postausgangs (etwa Einladung, Erinnerung oder Bescheid) zweimal erhalten. Inhalt und fachlicher Zustand bleiben richtig; es entsteht weder ein zweiter Auftrag noch eine zweite Abgabe. Belegt durch HearingOutboxDispatchTests.HungTransport_IsReleasedAfterGracePeriod_AndRetriedExactlyOnce (zwei Sendeaufrufe) und HearingResponseReceiptSqlServerTests.A_transport_without_any_answer_is_observed_once_more_then_given_up_and_the_order_is_taken_over (zwei Zustellversuche). Angenommen, Entscheidung von person-001 am 11.10.2026 (RAL-QM-HP-35 O6, RAL-QM-HP-37, Abschnitt Umsetzung); Szenario TV-04 in den Qualitätsanforderungen ist entsprechend korrigiert. Schließen ließe sich die Lücke nur mit einem Idempotenzschlüssel beim Provider oder einem Deduplizierungs-Header, den der Empfangsweg auswertet; beides ist nicht geplant.
BrowserGate: hearing-workflow-Schritt create-form — erledigt am 2026-09-18 Der End-to-End-BrowserGate lief bis tief in die Fachworkflows grün, brach aber im Schritt create-form des Hearing-Workflows (UI-Interaktion „Anhörung anlegen") mit einem Timeout ab. Kein Rename-Rest, sondern ein eigenständiges UI-Thema. Der volle Quality-Gate-Lauf war für diesen einen Schritt nicht grün; die Anhörungs-Erstellung war über den BrowserGate nicht automatisiert nachgewiesen. Nicht mehr reproduzierbar. Im vollständigen Lauf vom 2026-09-18 (41 von 41 Stufen, 1449 s, Browser gate PASSED, Cohesion 3.6.3-alpha.0.12) ist hearing-workflow in 8 s grün, ebenso in zwei vorangegangenen Läufen. Damit war es ein Lastbefund, wie der Eintrag selbst als erste Erklärung vorsah — siehe die Startprüfung in Getting Started. Die mitgeführte Frage nach der Circuit-Familie ist getrennt beantwortet und nur halb erledigt: die U16-Signatur (cohesion.resizer.detach, CohesionTextAreaInput.DisposeAsync) tritt im ganzen Lauf nicht mehr auf, und die Browserkonsole meldet über 44 geprüfte Seiten null Fehler. Die N6-Hälfte reproduziert weiterhin, 20-mal, im Hostprotokoll: TaskCanceledException aus JSRuntime.InvokeAsync über SfBaseUtils.InvokeMethod aus SfSwitch<T>.ComponentDispose (12×), SfCheckBox<T>.ComponentDispose (4×) und SfStepper.ComponentDispose (4×), jeweils als „Unhandled exception in circuit". Sie erreicht die Konsole nicht und rötet kein Gate, bleibt aber als Vendor-Lifecycle-Punkt offen (N6 → U16 im Stufe-6-Register).
Lokaler Keycloak-Port 8080 kollidiert — erledigt am 2026-09-18 Der lokale Entwicklungsstack bindet Keycloak per Default auf Port 8080. Auf mindestens einer Entwicklungsumgebung belegt ein unabhängiger guacamole-Container denselben Port, wodurch Keycloak nicht binden kann und der BrowserGate-Lauf umgebungsblockiert abbricht. Kein Projektfehler, aber ein reproduzierbarer lokaler Blocker für Setup/BrowserGate auf betroffenen Workstations. Gegenstandslos seit der Stackisolation (CAF-REPO-03). Der Standardport ist 18080 (etc/docker/{development,local}/.env.example), und tools/_stack_context.py vergibt KEYCLOAK_PORT je Stack dynamisch aus dem ephemeren Bereich gegen die Sperrliste in tools/stack-ports.json. Ein Fremdcontainer auf 8080 kann den Stack nicht mehr blockieren.
Secret-Rotation der alten Remote-URL Das origin-Remote des Quellrepositorys enthielt einen Zugriffstoken in der URL und wurde beim Umbau entfernt; das heutige origin (https://gitlab.com/development174835/caf) enthält keines. Falls dieser Token noch nicht widerrufen/rotiert wurde, ist das eine offene externe Sicherheitslücke außerhalb des Repository-Inhalts. Ein potenziell noch gültiges Secret außerhalb der Kontrolle des Repositorys. Rotation/Widerruf des Tokens außerhalb des Repositorys durchführen (GitLab-Zugriffstoken-Verwaltung); im Repository selbst ist kein Secret mehr enthalten und nichts weiter zu tun.
Immutabilität historischer Migrationen Historische EF-Migrationen behalten bewusst ihre alten Bezeichner (Caf*, QualityMarks*, Gz*), auch nachdem Live-Code und -Typen auf CAF*/RALQM umbenannt wurden. Tests wie DocumentsMigrationBaselineSqlServerTests erwarten explizit die alte Migrations-Id/-Quelle (AddCafDocumentShareGrants/CafDocumentShareGrants). Bewusster, dauerhafter Bruch zwischen aktueller Namenskonvention und historischem Migrationsverlauf; erschwert oberflächliches „alles muss RALQM/CAF heißen"-Refactoring und verlangt Disziplin bei neuen Migrationen. Kein Nacheditieren bestehender Migrationen. Neue Umbenennungen erfolgen ausschließlich über zusätzliche, datenbewahrende Rename-Migrationen (siehe 20260825174437_RenamePersistedIdentifiersToRALQM); Tests, die historische Bezeichner erwarten, bleiben bestehen.
RALQM-Datenmigration gegen reale Upgrade-Pfade unvalidiert Die manuelle SQL-Migration 20260825174604_MigrateApplicationIdentifiersToRALQM (Permissions, Grants, Settings, Text Templates, Lokalisierung, Rollen) war laut Übergabeprotokoll noch nicht vollständig gegen einen echten SQL-Server-Upgradepfad geprüft; zwischenzeitlich wurde sie gegen eine migrierte Dev-Datenbank verifiziert (Git-Historie, Arbeitsschritte R8/R5), ein vollständiger Produktions-Upgradepfad-Nachweis steht aber weiterhin aus. Restrisiko bei einem realen Upgrade eines bestehenden Produktivbestands auf das RALQM-Schema. Vor jedem produktionsnahen Einsatz erneut gegen eine repräsentative Kopie eines bestehenden Bestands migrieren und die in Qualitätsanforderungen beschriebenen SQL-Integrationstests plus Pending-Model-Check ausführen.
Institutionszugang: verbleibende Integration RAL-QM-HP-31 ersetzt Gast-/Claim-Pfade durch feste Kontobindung; historische Daten vermitteln kein Recht. Teilweise erledigt (2026-09-18): Der Browsernachweis ist mit Korrektur-MR !109 geliefert; die Spec SPEC-RAL-QM-HP-31-konto-an-institution-binden.md steht auf „Vollständig umgesetzt". Die Aussage „Browserstrecke ungeprüft (NOT_REQUESTED)" gilt nicht mehr. Ausführbare Service-/SQL-/Hostnachweise der RAL-QM-HP-31-Spec verwenden; verbleibende RAL-QM-HP-52-/RAL-QM-HP-13-/RAL-QM-HP-30-Abnahmen getrennt führen.
Verschlüsselter Portal-Link bleibt in nicht versandten Postausgangszeilen Zeilen berechtigungstragender Nachrichtenarten tragen den umkehrbar verschlüsselten Portal-Link (HearingOutboxMessage.ProtectedPayload), bis MarkSent ihn leert. Scheitert der Versand endgültig oder wird die Zeile aufgegeben, bleibt der Link unbegrenzt gespeichert. Seit RAL-QM-HP-9 legt jede Aussendung an einen Vertretenen eine zweite Zeile mit demselben Link an, sodass sich solche Zeilen verdoppeln können. Abweichung von der Regel, dass die umkehrbare Zwischenablage zeitlich begrenzt ist und nach Verwendung geleert wird (Entwicklungsleitfaden §10); ein Datenbankzugriff legt die Links fehlgeschlagener Zeilen offen. Eigene Sicherheitsfrage, nicht Teil von RAL-QM-HP-9: Payload fehlgeschlagener oder aufgegebener Zeilen nach einer Frist leeren. Zu klären ist, dass eine manuelle Wiederholung dann eine neu ausgestellte Einladung braucht (RequeueForRetry mit {{Link}} ohne Payload lässt sonst den ganzen Batch scheitern).
Vollständige Verifikationskette nach Umbau offen Root- und App-Build, vollständige Architekturtests, dotnet test CAF.slnx, Pending-Model-Check, SQL-Integrationstests und das komplette Quality Gate inklusive BrowserGate/Clean Room müssen nach den letzten Änderungen noch einmal vollständig und am Stück laufen, bevor der Umbau als abgeschlossen gilt (Punkt 1 der Umbau-Historie in docs/history/VOR-ERSTEM-PUSH.md). Ohne diesen Gesamtlauf ist „grün" nur stückweise für Teilmengen nachgewiesen, nicht als Gesamtsystemzustand. Teilweise erledigt (2026-09-18) — die Browserhälfte ist geliefert, die Kette nicht. Der BrowserGate ist erstmals vollständig grün gelaufen: 41 von 41 Stufen in 1449 s, console-verdict und accessibility-verdict eingeschlossen, 44 Seiten von axe geprüft, 0 Befunde (Branch fix/browsergate-ablehnungsroute auf development, Cohesion 3.6.3-alpha.0.12). Damit ist die Aussage „Browserstrecke ungeprüft (NOT_REQUESTED)" aus !121 !119 !116 !108 !101 !90 !88 gegenstandslos. Offen bleibt der Rest des Eintrags: Build, vollständige Architekturtests, dotnet test CAF.slnx, Pending-Model-Check, SQL-Integrationstests und Clean Room sind damit nicht belegt — sie liefen an diesem Tag nur stückweise und nicht am Stück, was dieser Eintrag ausdrücklich verlangt. Weiterhin gilt: vor jedem Merge/Release den vollständigen Quality Gate gemäß Entwicklungsleitfaden laufen lassen; Ergebnis nicht aus Einzelläufen extrapolieren — auch nicht aus diesem.
Serverseitiger main-Schutz nicht nachgewiesen — belegt am 2026-09-18 Remote und Merge-Request-Weg sind aktiv: origin zeigt auf https://gitlab.com/development174835/caf, main und development sind gepusht, jeder Release erreicht main als Merge-Commit. Ob GitLab Direct-/Force-Push auf main blockiert und die Review-Pflicht erzwingt, ist im Repository nicht feststellbar; die Randbedingungen beschreiben den Schutz als gesetzt. Ohne die serverseitige Einstellung ist die dokumentierte Git-Randbedingung eine Absicht, keine Kontrolle: ein Direct-Push auf main bliebe möglich. Nachgewiesen, damit entfällt diese Zeile wie im Eintrag vorgesehen. Abfrage der GitLab-API (projects/.../protected_branches): main und development sind geschützt, Push und Merge jeweils nur Maintainers, allow_force_push=false. Die Randbedingung aus conventions/git.md ist damit serverseitig belegt, nicht nur beabsichtigt.
Ein Cohesion-Publish kann den Build röten, ohne Änderung hier Cohesion wird mitentwickelt und deshalb bewusst nicht gepinnt, sondern über die Floating-Version 3.*-* aus dem GitLab-Gruppen-Feed bezogen (Directory.Packages.props) — höchste verfügbare 3.x inklusive Prereleases. Zwei Restores desselben Commits können unterschiedliche Versionen auflösen. Angenommener Trade, keine Schuld: dieser Build ist damit ein fortlaufender Integrationstest gegen Cohesion — Regressionen fallen früh auf statt beim nächsten bewussten Upgrade. Preis: ein CI-Lauf ist nicht allein durch den Commit bestimmt, und ein roter Build kann eine fremde Ursache haben. Nicht sperren (ein Lockfile verschöbe die Entdeckung), sondern sichtbar machen: aufgelöste Cohesion-Version in start.cmd/doctor.cmd und im CI-Log ausgeben. Bei unerwartet rotem Build zuerst die Version prüfen, dann eigenen Code. Siehe UI-Rework-Plan, Stage 0.
Stellungnahme mit Anlagen: Blockaden beim gleichzeitigen Speichern (RAL-QM-HP-38) Die Speicherphase einer Abgabe (Laufzeitsicht §6.4) hält drei Sperren, an denen sich zwei gleichzeitige Abgaben blockieren können: die Lesephase unter Serializable hält Bereichssperren auf RALQMHearingParticipants über die eigenen Zeilen hinaus; jede gespeicherte Datei ändert dieselbe Containerzeile des Datenbank-BLOB-Providers (AbpBlobContainers, betrifft auch Ablage und Unterlagen); jede Abgabe schreibt die Verfahrenszeile. Gemessen am 25. und 28.09.2026 gegen SQL Server ohne Wiederholung: zwei Abgaben mit je zehn Anlagen – zwei Institutionen desselben Verfahrens, zwei Konten derselben Teilnahme oder zwei Verfahren – in sieben bis zehn von zehn Versuchen ein Deadlock, bei fünf wie bei 5.000 Teilnehmerzeilen; zwei gleichzeitige Uploads allein über die Containerzeile in zehn von zehn. Die frühere Existenzprüfung vor dem Einfügen war eine vierte Ursache (60 von 60) und ist entfernt. Ohne Gegenmaßnahme erhielte eine von zwei gleichzeitigen Abgaben eine wiederholbare Meldung; Daten gehen nicht verloren (vollständiger Rollback, HearingResponseSubmissionSqlServerTests). Anlagen sind nur auf dem Datenbank-BLOB-Provider zulässig (Festlegung RAL-QM-HP-38). Die Datei einer gescheiterten Speicherung entfernt auf dem Dateisystem-Provider seit RAL-QM-HP-51 der Documents-Baustein (Zeile unten); was ein Prozessabbruch dazwischen zurücklässt, seit RAL-QM-HP-81 der Abgleichslauf. Die Dateien einer Abgabe liegen während des Aufrufs vollständig im Arbeitsspeicher – bei der Höchstzahl 10 und 25 MiB je Datei bis 250 MiB je gleichzeitiger Abgabe. Die Speicherphase wird als Deadlock-Opfer bis zu zweimal mit den gelesenen Dateien wiederholt (HearingResponseStorageRetry, Pause 100 ms mal Versuch); erst danach die wiederholbare Meldung, Eingaben und Dateien bleiben im Formular. Die Ursachen selbst sind nicht Teil von RAL-QM-HP-38: Jira-Vorgang „Blockaden beim gleichzeitigen Speichern klären“ (Bereichssperren der Lesephase, Containerzeile, Verfahrenszeile); das einzelne Speichern der Dateien lohnt, sobald Documents löschen kann. Höchstzahl und Größe je Datei bleiben Konfiguration; die Höchstzahl je Stellungnahme ist die Hearings-Einstellung ResponseAttachmentMaxCount.
Dateiinhalt bei Prozessabbruch und Begleitbrief des Idempotenz-Verlierers (RAL-QM-HP-51, RAL-QM-HP-24) Seit RAL-QM-HP-51 entfernt der Documents-Baustein mit dem Dateisystem-Provider den Inhalt jeder Erstellung über IDocumentContentPort.CreateAsync, deren Unit of Work scheitert – Rollback, Fehler beim Speichern oder beim Commit selbst (Querschnitt, Documents-Vertrag); das betrifft Abschlussbericht, Begleitbriefe und Anlagen einer Stellungnahme. Seit RAL-QM-HP-81 (Umsetzung 12.10.2026) ist die prozesslokale Bereinigung nicht mehr die einzige: Ein wiederkehrender Abgleichslauf entfernt jede Datei ohne committeten Deskriptor, die älter als die Mindestzeit ist (Standard 24 Stunden) – auch nach einem Prozessende zwischen Schreiben und Bereinigung oder einer Bereinigung, die die Datenbank nicht erreichte. Der Begleitbrief eines Versandauftrags, der das Rennen um seinen Idempotenzschlüssel verliert, wird in dessen Unit of Work wieder gelöscht (vorher Altbestand RAL-QM-HP-24). Verbleibend: Bis zum nächsten Lauf nach Ablauf der Mindestzeit liegt ein Rest im Dateispeicher; ein Mandant ganz ohne Deskriptor wird nicht abgeglichen; ein Löschen, das scheitert, hinterlässt einen echten Rest (als Fehler mit Inhalts-Id und Fehlerklasse gemeldet). Seltene unreferenzierte Dateien im Dateispeicher, nach Ablauf der Mindestzeit beim nächsten Lauf entfernt: über Verfahren und Portal nicht erreichbar, bis dahin ein Speicher- und Datenschutzrest außerhalb jeder fachlichen Sicht. Standardbetrieb mit dem Datenbank-BLOB-Provider. Abgleichslauf und Verlierer-Löschung RAL-QM-HP-81 (Documents-Vertrag); Fehler-Logeinträge des Documents-Bausteins sind echte Reste und betrieblich zu bearbeiten. Die Fehlerzweige von Abschluss und Upload (Löschen über IDocumentFilingPort) bleiben als zusätzliche Absicherung bestehen; sie sind neben der Bereinigung redundant (wirksam nur bei einer Abweisung vor dem Speichern, bei Speicher- oder Commitfehler scheitern sie und protokollieren eine Warnung) und durch Bestandstests abgesichert.
Feed-Zugang ist neue Voraussetzung für jeden Checkout Cohesion liegt nicht mehr als .nupkg im Repository. Ohne Zugangsdaten für die Quelle cohesion schlägt bereits dotnet restore fehl. Ein frischer Checkout ist ohne Gruppen-Deploy-Token (lokal) bzw. Job-Token-Allowlist (CI) nicht baubar; Offline-Builds sind nicht mehr möglich. Onboarding-Schritt in Getting Started und doctor.cmd-Prüfung (SET/MISSING); start.cmd bricht mit dem exakten Konfigurationsbefehl ab statt mit einem NuGet-401.

Bewusst nicht als Schulden geführt

Die folgenden Punkte sind laut Qualitätsanforderungen §10.3 und Kontextabgrenzung §3.4 bewusste Auslassungen, keine offenen Risiken: kein lokaler OpenIddict-Server, keine Microservices, keine zweite GZ-Datenbank, kein PostgreSQL/Redis/RabbitMQ/Kafka/Elsa/GraphQL/Kubernetes, kein externes DMS, keine Dokumentbearbeitung/OCR/Volltextsuche/Legal-Archiv/generische Dateibrowser-UI. Ein neuer Bedarf in diese Richtung erfordert zuerst einen konkreten, getesteten Anwendungsfall, keine vorsorgliche Abstraktion.

Verwandte Kapitel

  • Qualitätsanforderungen — Testarten und Gate-Aufbau, gegen die die hier gelisteten Lücken laufen.
  • Bausteinsicht — Modulgrenzen und Paketierung, insbesondere zum Cohesion-Pin.
  • Entwurfsentscheidungen — Begründung der RALQM-/CAF-Präfixregel und der datenbewahrenden Migrationsstrategie.
  • Entwicklungsleitfaden und Getting Started — operative Schritte für lokale Infrastruktur und Quality Gate.

12. Glossar

Dieses Kapitel bündelt die verbindlichen Begriffe der Lösung. Fachbegriffe stammen aus dem RAL-Gütezeichen-Anhörungsprozess, technische Begriffe aus der No-Layer-/ABP-Architektur des Monorepos. Details zu den referenzierten Bausteinen stehen in der Bausteinsicht, Randbedingungen und Namenskonventionen in den Randbedingungen; das normative Regelwerk bleibt DEVELOPMENT-GUIDE.md und conventions/.

Fachliche Begriffe

Begriff Definition
Anhörung (Hearing) Das zentrale Verfahren: eine Institution wird zu einer geplanten Gütezeichen-Entscheidung angehört. Durchläuft die benannte Statuskette Draft → PendingApproval → Approved → Dispatched → Adjudication → Completed mit definierten Rücksprüngen/Abbruch. Eigenes Aggregat im Modul Hearings, Tabellen RALQMHearings/RALQMHearing*.
Ansprechpartner Ein Kontakt einer Institution (RALQMInstitutionContacts). Seine Funktion ist seit RAL-QM-HP-4 kein Freitext mehr, sondern eine optionale Referenz (FunctionId) auf eine Ausprägung der Werteliste „Funktion eines Ansprechpartners“; ohne Angabe bleibt der Kontakt ohne Funktion. Der Bestand dieser Liste entstand vollständig aus den vorhandenen Positionstexten — sie hat, anders als die drei übrigen Listen, keinen ausgelieferten Grundbestand. Wird ein Ansprechpartner einem Verfahren hinzugefügt, heißt er dort Teilnehmer. Seit RAL-QM-HP-9 trägt ein Kontakt zusätzlich eine optionale Telefonnummer, das Kennzeichen Standardansprechpartner und wahlweise eine Vertretung (je eigener Eintrag).
Standardansprechpartner (RAL-QM-HP-9) Der eine Kontakt je Institution, der das Kennzeichen IsDefault trägt: höchstens einer, und er ist immer aktiv. Hat eine Institution keinen Standard, wird der erste erfasste oder aktivierte aktive Kontakt selbsttätig Standard (A3) — beim Anlegen, im CSV-Import und im Demo-Seed über dieselbe Aggregatoperation. Der Wechsel ist eine Operation des Aggregats (Institution.SetDefaultContact) und wird in einer Transaktion als zwei Speicherschritte gespeichert (alten Standard entfernen, speichern, neuen setzen, speichern), weil der gefilterte eindeutige Index IX_RALQMInstitutionContacts_DefaultPerInstitution je Statement prüft und EF die beiden Zeilen in Schlüsselreihenfolge schreibt; für andere Verbindungen gibt es keinen Zustand ohne Standard. Der Standard kann weder deaktiviert noch entfernt werden, solange kein anderer aktiver Kontakt das Kennzeichen übernommen hat; das Pflichtpartner-Kennzeichen setzt einen aktiven Standard voraus (RAL-QM-HP-10 AC-11). Gelesen wird das Kennzeichen vom Pflichtpartner-Port und von Hearings bei der automatischen Einbindung von Pflichtpartnern sowie als Ersatz beim Kopieren eines Verfahrens; dort bleibt der zuvor gewählte aktive Kontakt vorn (E10).
Vertretung (RAL-QM-HP-9) Verweis am Kontakt (DeputyContactId, DeputyFromUtc, DeputyUntilUtc) auf einen anderen aktiven Ansprechpartner derselben Institution mit Beginn und wahlweise Ende als UTC-Kalenderdatum, beide Ränder eingeschlossen (A10). Höchstens eine Vertretung je Kontakt; eine Person darf mehrere vertreten; keine Selbstvertretung, keine Ketten (die Vertretung der Vertretung erhält nichts). Wirksam, solange die Vertretung aktiv ist und der heutige Tag im Zeitraum liegt (Institution.FindEffectiveDeputy); die Deaktivierung des Vertretenen beendet sie nicht (A11). Ein als Vertretung eingetragener Kontakt kann nicht entfernt werden. Wirkung: eine unveränderte Kopie jeder Aussendung an den Vertretenen an die E-Mail-Adresse der Vertretung — als eigene Postausgangszeile, die HearingOutboxManager beim Einstellen jeder Nachrichtenart in derselben Transaktion und Batch-ID anlegt (Schlüsselsuffix :dep, derselbe verschlüsselte Link; keine Kopie bei gleicher Adresse, E12), sichtbar als weitere Zeile im Zustellstatus (nach der Hauptzeile) und in der Postausgangsverwaltung (Details).
Antragsteller Die Institution oder Person, deren Gütezeichen-/Zulassungsantrag Gegenstand der Anhörung ist. Wird bei Anlage der Anhörung über eine InstitutionId referenziert, nicht dupliziert, und muss zu diesem Zeitpunkt eine aktive Institution sein (RAL-QM-HP-10, AC-9).
Frist Zeitliche Grenze im Anhörungsprozess: erste und zweite Rückmeldefrist und die Frist bis zur Abschlusserinnerung, dazu die daraus abgeleiteten fälligen Aktionen (Due Actions). Beide Rückmeldefristen werden kumulativ ab demselben Befragungsbeginn gemessen — die zweite ist die Gesamtdauer, kein Zuschlag auf die erste, und muss größer als diese sein (Auslieferungswerte 56/84 Tage). Die Vorbelegung eines neuen Verfahrens kommt aus den Standardfristen seiner Verfahrensart, sonst aus den systemweiten Einstellungen, sonst aus den Auslieferungswerten; sie erreicht die Fachkraft als überschreibbarer Formularvorschlag. Ein Versand wird abgewiesen, wenn eine der beiden Rückmeldefristen bereits abgelaufen ist. Die zweite Frist bleibt im laufenden Verfahren in beide Richtungen änderbar (solange in der Zukunft und nach der ersten) — deshalb heißt die Karte „Zweite Frist ändern“ und nicht mehr „Frist verlängern“ —, bis eine Erinnerung sie genannt hat: ab deren Versand ist sie festgeschrieben (RAL-QM-HP-29 AC-7, SecondResponseDeadlineFixedAtUtc). Fristüberwachung läuft über persistente Due Actions und ABP Background Worker, nicht über Timer im Request.
Gütezeichen (Quality Mark) Das RAL-Gütezeichen, dessen Verleihung/Verlängerung/Entzug den fachlichen Anlass für eine Anhörung bildet. Gibt der Anwendung ihren Domänennamen (QualityMarks) und den Kürzel RALQM.
Institution Der fachliche Träger von Kontakten, Anhörungen und Pflichtpartnerbeziehungen. Eigenes Aggregat im Modul Institutions mit Anschrift, Kreis (Fachkreis oder Verkehrskreis), Pflichtpartner-Kennzeichen (seit RAL-QM-HP-10 ein eigenes Ja/Nein-Merkmal, kein dritter Kreis), interner Notiz (nur für interne Benutzer sichtbar, RAL-QM-HP-10) und mehreren Kontakten. Tabellen RALQMInstitutions/RALQMInstitutionContacts.
Dublette (Institution) (RAL-QM-HP-10) Zwei Institutionsnamen gelten als gleich, wenn sie nach Entfernen aller Leerzeichen ohne Beachtung der Groß-/Kleinschreibung übereinstimmen (InstitutionNameDuplicateRule, eine Stelle in der Institutions-Runtime). Anlage und Umbenennung warnen mit Trefferliste, sperren aber nicht; kein Unique-Index. Der CSV-Import vergleicht bis RAL-QM-HP-49 weiterhin nur ohne Groß-/Kleinschreibung.
Laufendes Verfahren (Deaktivierungswarnung) (RAL-QM-HP-10) Für die Zählung vor der Deaktivierung einer Institution gilt eine Anhörung als laufend, solange sie weder abgeschlossen (Completed) noch abgebrochen (Cancelled) ist; Entwürfe zählen mit. Je Verfahren zählt die Institution einmal, wenn sie Antragsteller oder nicht zurückgezogener Teilnehmer ist (SPEC-RAL-QM-HP-10 A4). Bewusst enger als der Löschschutz, der jede jemals vorhandene Teilnehmer-Referenz zählt.
Institutionsgruppe (RAL-QM-HP-3) Ein verfahrensübergreifend wiederverwendbarer, benannter Beteiligtenkreis aus mehreren Institutionen, gemeinsames Stammdatum aller internen Konten. Aggregat InstitutionGroup mit den Tabellen RALQMInstitutionGroups und RALQMInstitutionGroupMembers; Name eindeutig nach der Dublettenregel, Auflösen löscht die Gruppe weich und ihre Mitgliedschaften hart. Als Ganzes in ein Verfahren im Entwurf einbindbar seit RAL-QM-HP-5 (US-13, Gruppenzuordnung: Vorschau, dann ein Speichervorgang für alle zuordenbaren Mitglieder; Übersprungene werden mit Grund genannt); für Rundnachrichten adressierbar (US-41) ist sie noch nicht. Siehe Bausteinsicht §5.2.2 und §5.2.3.
Mitgliedschaft (Institutionsgruppe) (RAL-QM-HP-3) Die Zuordnung genau einer Institution zu genau einer Gruppe (InstitutionGroupMember, eindeutig je Gruppe und Institution). Sie trägt außer den beiden Verweisen keinen Inhalt und wird beim Entfernen hart gelöscht; ihr vollständiger Stand bleibt in der Entity History. Eine deaktivierte Institution bleibt Mitglied, eine gelöschte fällt still aus Liste und Zählung.
Kennzeichnungsart Die Art der RAL-Kennzeichnung, um die es im Verfahren geht — ausgeliefert werden Gütezeichen, Registrierungen, Definierte Herkunft von Lebensmitteln und Made in Germany. Pflichtangabe je Verfahren, referenziert über MarkTypeId auf RALQMHearings. Bis RAL-QM-HP-4 ein C#-Enum mit Localization-Schlüsseln, seitdem eine Werteliste.
Referent Produktrolle RALQMCaseOfficer: bearbeitet Anhörungen operativ (anlegen, einreichen, Teilnehmer/Dokumente pflegen, Postausgang, Vorlagen), pflegt die Institutionsstammdaten (anlegen, ändern, deaktivieren; kein Löschen, RAL-QM-HP-10), übermittelt die Bescheidungen und schließt das Verfahren ab, direkt wie einstufig (Adjudicate, Complete; RAL-QM-HP-46 Regel 1/O1, RAL-QM-HP-51 O2). Erfassen, Übermitteln und Abschließen liegen damit bewusst in einer Rolle; das Vier-Augen-Prinzip betrifft Einreichung und Freigabe. Er darf aber z. B. nicht die von ihm eingereichte Freigabe selbst erteilen (Vier-Augen-Prinzip) und besitzt kein Abbruchrecht für laufende Verfahren.
Zuständiger Referent (RAL-QM-HP-8) Die benannte Person, die für ein Verfahren verantwortlich ist: Feld ResponsibleUserId des Hearing-Aggregats (lokale ABP-Benutzer-Id, kein Fremdschlüssel in die Identitätstabellen), bei der Anlage der anlegende Benutzer, danach über „Zuständigkeit übertragen“ (eigenes Recht RALQM.Hearings.TransferResponsibility, Referent und Geschäftsführung) auf ein aktives, Keycloak-gebundenes Konto mit Rolle Referent verschiebbar, solange das Verfahren nicht abgeschlossen oder abgebrochen ist. Jede Übertragung ist ein Verlaufseintrag (ResponsibilityTransferred) mit bisheriger und neuer Person. Altbestand ohne Ersteller trägt null und liest sich als „nicht zugewiesen“ — kein Sentinel, der eine Person vortäuscht. Namen werden über den schmalen Lesevertrag IStaffAccountReferenceReader des Authorization-Moduls aufgelöst; das Adressfeld für künftige interne Benachrichtigungen (RAL-QM-HP-35, RAL-QM-HP-43).
Rolle Produktweite GZ-Rolle, die Grants aus den Permissions der Feature-Contracts komponiert. Drei Rollen: RALQMAdministrator, RALQMCaseOfficer (Referent), RALQMManagement (Geschäftsführung). Rollen- und Grant-Komposition liegt ausschließlich im Modul Authorization.
Geschäftsführung (Management) Produktrolle RALQMManagement: erteilt u. a. die Freigabe (andere Person als die Einreichung) und die Nachversand-Freigabe und darf laufende Verfahren abbrechen; besitzt kein Referentenrecht auf Abbruch. Bescheidungsübermittlung und Abschluss gehören seit RAL-QM-HP-51 nicht mehr zu ihren Rechten (RAL-QM-HP-46 O1, RAL-QM-HP-51 O2).
Teilnehmer (Participant) Ein zur Anhörung hinzugefügter Kontakt einer Institution samt Snapshot der Kontaktdaten zum Versandzeitpunkt. Kann im Entwurf zurückgezogen und wiederaufgenommen werden, ohne die Historie zu verlieren; nach dem Versand bleibt die Institution beteiligt, und der Referent kann stattdessen ihren Zugang entziehen (seit RAL-QM-HP-5). Trägt seit RAL-QM-HP-5 einen Beteiligungsgrund und seit RAL-QM-HP-6 den operativen Empfängerstand (Institution mit Anschrift, Kontakt mit Anrede, Titel, Name, Funktion, E-Mail), den jede neue Aussendung verwendet und den nur ein ausdrücklicher Befehl des Referenten ändert.
Kommunikationsschritt Der bearbeitbare Wortlaut einer Aussendung je Verfahren und Zweck (RAL-QM-HP-24): einmalige Kopie des gebundenen Vorlagentexts, mit Institutionsabweichungen, gesperrt für das erste Anschreiben ab der Einreichung und für jeden Zweck ab der ersten Postausgangszeile.
Begleitdokument Das PDF-Anschreiben einer Teilnehmeraussendung (RAL-QM-HP-24): Briefkopf mit dem Organisationsabsender des Erzeugungsmoments, Anschrift aus dem Empfängerstand, Ansprechperson, Datum, Betreff, Wortlaut, Zeichnung; über CAF Documents abgelegt, als Anhang an die Nachricht derselben Beteiligung gehängt, von der Vertretungskopie geteilt. Gilt für jede Nachrichtenart außer der Endfassungsbenachrichtigung (FinalVersionDelivery, 70): Sie trägt seit RAL-QM-HP-51 (E4) kein Begleitdokument und keinen Anhang (zentrale Regel HearingCoverLetterRule); die Endfassung selbst wird nie angehängt, sondern im Portal abgerufen.
Endfassung Die Ansicht des Briefs und der Nachricht eines ausdrücklich gewählten Empfängers, wie die Aussendung sie erzeugen würde – eine reine Lesung mit OutputStamp, den der Erstversand zurückverlangt (RAL-QM-HP-24 AC-4). Nicht zu verwechseln mit der Endfassung des Verfahrens (RAL-QM-HP-51): den aktiven, nicht ersetzten Abschlussanlagen (Zweck ClosingAttachment), die der Abschluss den Endfassungsempfängern nach Regel 4 über Grants im Portal freigibt.
Rückmeldungsübersicht (Abschlussbericht) (RAL-QM-HP-51) Das interne PDF, das der Abschluss genau einmal je Verfahren erzeugt: Kopf mit Nummer, Titel, beiden Fristen und Erstellungszeit, danach je Beteiligung – auch stille und zurückgezogene – Status und Einträge in Eingangsreihenfolge (Eingangszeit, Art, Verspätung, Text, wirksame Bescheidung). Verfahrensdokument mit Zweck ClosureReport und Herkunft Generated, ohne Grants, nie Pflichtdokument, für keine Institution freigegeben (HearingPortalRules.IsDocumentReleased). Dateiname Rueckmeldungsuebersicht_<Nummer>.pdf, bei belegtem Namen mit deterministischem Suffix _2, _3 …
Empfängerstand Die Adressierungswerte einer Beteiligung (RAL-QM-HP-6). Der operative Stand liegt auf der Beteiligung und bestimmt neue Aussendungen; eine Stammdatenänderung ist nur eine Abweichung, bis der Referent sie übernimmt, den Empfänger wechselt oder die Adresse korrigiert. Der Versandstand ist der auf der Postausgangszeile eingefrorene Stand einer Aussendung und wird nie umgeschrieben.
Kontaktmeldung (RAL-QM-HP-41) Die Meldung einer anderen Kontaktperson durch eine Institution im laufenden Verfahren, je Beteiligung (HearingContactReport, Tabelle RALQMHearingContactReports). Sie ist offen, bis die Institution sie zurückzieht oder der Referent sie übernimmt (in die Stammdaten, als vorhandenen Kontakt oder nur für dieses Verfahren), durch eine andere Person ersetzt oder verwirft; je Beteiligung ist höchstens eine offen. Sie vergibt keinen Zugang und ändert kein Konto. Ein Empfänger „aus Kontaktmeldung, nur dieses Verfahren“ trägt keinen Stammdatenkontakt (ADR-14).
Beteiligungsgrund (RAL-QM-HP-5) Warum eine Institution an einem Verfahren beteiligt ist: Pflichtpartner oder manuell (HearingParticipationReason, Pflichtspalte ParticipationReason auf RALQMHearingParticipants, Werte 10 und 20, die 0 bleibt unbelegt). Entschieden im Moment der Zuordnung aus dem Pflichtpartner-Kennzeichen der Institution, auf jedem Weg – automatische Einbindung, Einzelzuordnung, Gruppenzuordnung, Kopiervorlage, Nachbeteiligung – und danach unveränderlich, auch wenn sich das Kennzeichen ändert (E2). Eine wiederaufgenommene Beteiligung behält ihn. Die Beteiligtenliste zeigt ihn als Text.
Sammelzuordnung (RAL-QM-HP-20) Ein bestätigter Auftrag des Referenten, der eine Unterlage allen im Moment des Speicherns aktiven Beteiligten zuordnet: je Beteiligung ohne aktiven Grant entsteht einer, bestehende bleiben, zurückgezogene werden übersprungen, ein wiederholter Auftrag erzeugt nichts. Die Unterlage merkt sich den Zeitpunkt (GrantedToAllParticipantsAtUtc); danach hinzugefügte Beteiligte erhalten sie nicht automatisch – anders als bei einer Pflichtunterlage –, sondern werden in der Verfahrensakte als nachzupflegen benannt.
Pflichtpartner (RAL-QM-HP-10, RAL-QM-HP-5) Kennzeichen einer Institution, unabhängig von Kreis und Verfahrensart, nur mit aktivem Standardansprechpartner setzbar. Jeder neue Entwurf bindet die gekennzeichneten, aktiven Institutionen selbsttätig ein; ihre Beteiligung trägt den Beteiligungsgrund Pflichtpartner. Das Zurückziehen einer solchen Beteiligung im Entwurf ist das „Ausnehmen“ eines Pflichtpartners – kein eigener Befehl, kein eigenes Recht, aber ein eigener Verlaufseintrag.
Abbruch (laufendes Verfahren) (RAL-QM-HP-42) Beendigung eines Verfahrens in Dispatched oder Adjudication durch die Geschäftsführung mit amtlicher Begründung (Hearing.Abort, Recht RALQM.Hearings.Abort): Endzustand Cancelled, IsCancellationReasonOfficial = true, Widerruf aller laufenden Zugänge, genau eine Abbruchmitteilung je angeschriebener Beteiligung (aktive einschließlich Zugangsentzug, zurückgezogene nur mit früherer Postausgangszeile) mit Begründung ({{Begruendung}}) und tokenfreiem Portallink. Abzugrenzen von der Stornierung vor dem Versand (Cancel, Draft/PendingApproval, ohne Nachricht, Grund intern) und vom Zurückziehen einer Beteiligung. Kein Rücksprung, kein neuer Bearbeitungszeitraum; Archivkennzeichen folgt mit RAL-QM-HP-44.
Archivkennzeichen (RAL-QM-HP-44) Eigenständiges Merkmal Hearing.IsArchived (mit ArchivedAtUtc, ArchivedByUserId) eines beendeten Verfahrens (Completed, Cancelled): entfernt es aus Verfahrensübersicht, Arbeitsvorrat und Export und macht es in der Archivansicht auffindbar; ändert weder Status noch Schreibschutz noch Zugänge. Setzen und Zurückholen (mit Pflichtbegründung, nur im Audit-Log als Zusatzeigenschaft ArchiveRestoreReason) unter dem Recht RALQM.Hearings.Archive (Referent, Geschäftsführung), je ein Verlaufseintrag. Nicht die „Ablage“ (Filing) und nicht die externe Archivreferenz des Abschlusses.
Eingangsbestätigung (RAL-QM-HP-35) Nachricht an die operative Kontaktadresse einer Beteiligung nach jeder im Portal gespeicherten Abgabe oder Ergänzung: Verfahren, Kategorie, Abgabezeit in UTC, Dateinamen mit SHA-256 der übernommenen Bytes und ein tokenfreier Portalverweis – nie der Stellungnahmetext. Parallel erhält der zuständige Referent einen Eingangshinweis mit Prüflink. Beide sind dauerhafte Aufträge mit Wiederholung und Störung; sie belegen die gespeicherte Abgabe, nicht den Postfacheingang.
Öffentliche Anzeige (RAL-QM-HP-47) Kennzeichen Hearing.IsPublished eines freigegebenen oder versendeten Verfahrens, gesetzt und zurückgenommen von Referent oder Geschäftsführung (RALQM.Hearings.Publish). Sichtbar ist ein Verfahren nur, solange Kennzeichen und Status Approved/Dispatched zusammen gelten; Bescheidung, Abschluss oder Abbruch beenden die Anzeige von selbst, die Freigaberücknahme nimmt zusätzlich das Kennzeichen zurück. Gezeigt werden nur Titel, Gütezeichen, Verfahrensart und Frist – anonym auf /anhoerungen/oeffentlich und für die Website über den Bestandsabruf.
Öffentliche Kennung (RAL-QM-HP-47) Hearing.PublicId: beim ersten Kennzeichnen vergebene, danach unveränderliche Kennung, unter der Website und Bestandsabruf ein Verfahren führen – über Rücknahme, erneute Anzeige und Entfernung hinweg. Weder Aktenzeichen noch Verfahrens-Id.
Bestandsabruf (RAL-QM-HP-47) Der authentisierte JSON-Abruf GET /api/gz/hearings/public eines zugelassenen Keycloak-Integrationsclients: ohne Cursor der vollständige sichtbare Bestand, mit changedSinceUtc die seitdem geänderten sichtbaren Verfahren und die Entfernungen (nur Kennung und Zeitpunkt). Abgleich je Kennung nach Änderungszeitpunkt.
Zugang entzogen (RAL-QM-HP-5) Zustand einer aktiven Beteiligung in einem laufenden Verfahren (Dispatched, Adjudication): Der Referent hat den Portalzugang der Institution zu diesem einen Verfahren entzogen (HearingParticipant.AccessRevokedAtUtc). Die Institution bleibt beteiligt – Status, Empfänger, Unterlagenzuordnungen, Fristen und die feste Kontobindung bleiben –, aber kein Konto der Institution erreicht das Verfahren im Portal, und solange der Zustand besteht, gehen keine fristgesteuerten Nachrichten an sie und lässt sich auch von Hand keine Einladung ausstellen oder erneuern. Ohne Begründung, ohne Nachricht an die Institution, mit Verlaufseintrag; „Zugang wieder freigeben“ hebt ihn ohne neue Einladung auf. Nach Abschluss oder Abbruch bleibt er bestehen und ist nicht mehr änderbar. Kein Zurückziehen (das nimmt die Institution aus dem Verfahren) und keine Kontosperre.
Verfahrensart Die Art des Anhörungsverfahrens — ausgeliefert werden neues Gütezeichen, Revision, Anerkennungsverfahren, Erweiterung und Satzungsänderung. Pflichtangabe je Verfahren (ProcedureTypeId). Die einzige Werteliste, die zusätzlich Daten trägt: je Ausprägung optional drei Standardfristen (erste und zweite Rückmeldefrist, Abschlusserinnerung), entweder vollständig hinterlegt oder gar nicht, die neue Verfahren als überschreibbare Vorbelegung übernehmen. Eine spätere Änderung der Standardfristen wirkt nur auf danach angelegte Verfahren. Nach Verfahrensart lassen sich die Verfahrenslisten seit dem Umbau nicht mehr serverseitig sortieren (siehe ADR-07).
Vier-Augen-Freigabe Fachregel: Einreichung und Freigabe einer Anhörung müssen von unterschiedlichen Personen (Akteurstrennung) erfolgen; die geprüfte Zusammensetzung wird über eine Kompositionsprüfsumme gesperrt.
Werteliste (Value List) Eine über die Oberfläche gepflegte Katalogtabelle mit den zulässigen Ausprägungen eines Feldes. Es gibt vier: Kennzeichnungsart, Verfahrensart, Land und Funktion eines Ansprechpartners. Jede Ausprägung trägt eine unveränderliche Guid-Kennung (das Einzige, was andere Module speichern), einen stabilen technischen Code, Anzeigenamen je Kultur (NameDe Pflicht, NameEn optional, Rückfall auf Deutsch), ein Aktivkennzeichen und eine Sortierung. Ausprägungen werden nie gelöscht, nur deaktiviert: An bestehenden Datensätzen bleiben sie sichtbar, für neue werden sie nicht mehr angeboten. Änderungen wirken ohne Neustart, weil die Listen nicht zwischengespeichert werden. Gepflegt wird unter /administration/wertelisten, ausschließlich durch RALQMAdministrator. Eigener Feature-Slice ValueLists mit den Tabellen RALQMMarkTypes, RALQMProcedureTypes, RALQMCountries und RALQMContactFunctions; siehe Bausteinsicht §5.2.4 und ADR-07.
Abschlussvariante (US-33) Je Verfahren gewählt: zweistufig durchläuft Bescheidung als eigenen Status vor dem Abschluss; einstufig versendet Bescheidung und Endfassung in einem Vorgang und überspringt den Adjudication-Status. Siehe Einführung und Ziele.
Bescheidung (Adjudication) Prozessphase nach den Rückmeldungen: Endfassungen werden in einem begrenzten, additiven Fenster zusammengestellt und individualisiert an aktive Teilnehmer versendet; das Fenster schließt mit dem ersten Bescheidungsversand. Entfällt als eigener Status bei einstufiger Abschlussvariante (siehe Abschlussvariante).
Stellungnahme (Response) Die fachliche Rückmeldung eines Teilnehmers im Portal über sein Benutzerkonto (übergangsweise noch Gastportal), ggf. mit Anhängen; kann innerhalb der Frist mit dem zuletzt gelesenen Concurrency-Stand geändert werden.
Verfahrensverlauf (RAL-QM-HP-7) Die append-only Zeitleiste eines Verfahrens (RALQMHearingHistoryEntries): je verfahrensrelevantem Ereignis genau ein Eintrag mit Ereignisart aus dem geschlossenen Katalog HearingHistoryEventType, Zeitpunkt aus IClock, Aktor (interne Person, Teilnehmer, gebundenes Konto) und betroffenem Kindobjekt. Der Urheber wird beim Lesen aufgelöst: die Person namentlich, „System“ für die zeitgesteuerte Verarbeitung (Erinnerung, Fristablauf), bei institutionsbezogenen Ereignissen die Institution mit Namen. Einträge sind unveränderlich — der Persistenzpfad weist Update und Delete ab (HistoryEntryImmutable). Die Zeitleiste ist nach Ereignisart, Institution und Zeitraum einschränkbar und bei abgeschlossenen und abgebrochenen Verfahren unverändert einsehbar.
Datenänderung (RAL-QM-HP-7) Ein Eintrag der Zeitleiste, der ein protokollpflichtiges Feld des Verfahrens mit altem und neuem Wert nennt (Titel, Beschreibung, Kennzeichnungsart, Verfahrensart, antragstellende Stelle, Fristen, Abschlussvariante, Zuständiger, Archivreferenz, Abschlussnotiz, Begründung). Quelle ist die ABP Entity History des Verfahrens (RALQMHearingsEntityHistory), nicht die Verlaufstabelle (die PII-frei bleibt); Kennungen werden zu Namen aufgelöst. Gehört der Feld-Diff zum Ereignis derselben Operation (z. B. Titel zu „Titel geändert“), hängt er an diesem Eintrag, sonst steht er als eigener Eintrag „Datenänderung“. Institutionen, Vorlagen und Fristvorgaben führen ihren Feld-Diff in der Entity History des Audit-Protokolls.
Verwendungszweck (RAL-QM-HP-21) Der Verfahrensschritt, dem eine Verfahrensunterlage dient — genau einer je Unterlage: Anhörungsunterlage (erstes Anschreiben; nur im Entwurf anhäng-, änder- und entfernbar, ab der Einreichung zur Freigabe eingefroren, nach Rückzug oder Ablehnung wieder frei), Bescheidungsanlage (Bescheidungsschritt bis zum Bescheidversand) und Abschlussanlage (ab dem Anhörungsversand bis zum Abschluss). Feld Purpose des HearingDocument (HearingDocumentPurpose), entscheidet das Fenster für Anhängen, Anzeigeangaben, Zweckwechsel, Zuordnung und Entfernen (Hearing.IsDocumentWindowOpen) und die Bereitstellung im Portal je Schritt (HearingPortalRules.IsDocumentReleased). Hat das frühere Kennzeichen „Bescheidungs-Endfassung“ (IsAdjudicationFinal) aufgenommen; die Dokumentrolle (Anschreiben, Gegenstand, Nachweis, Anlage) bleibt die Art der Unterlage.
Herkunft (RAL-QM-HP-21) Woher eine Verfahrensunterlage stammt — Feld Origin des HearingDocument mit vier persistierten Werten (HearingDocumentOrigin): Upload (Arbeitsplatz-Upload), Generated (erzeugtes Schreiben), Submitted (eingereichte Anlage) und Archive, im Deutschen „Ablage“. Erzeugte Schreiben und eingereichte Anlagen sind nie entfernbar. Der Wert Archive entstand mit RAL-QM-HP-21 zusammen mit den drei anderen, blieb dort aber ohne Erzeuger; seinen ersten und bis heute einzigen Erzeuger hat er mit der Übernahme aus RAL-QM-HP-16 bekommen. Identifier und Anzeigetext sind also nicht dasselbe: Archive ist der Code, „Ablage“ der deutsche Text.
Gestört (RAL-QM-HP-21) Kennzeichen einer Verfahrensunterlage in der Dateiübersicht, deren Inhalt der Dokumentenspeicher nicht mehr beschreibt (kein Descriptor zur DocumentId). Serverseitig je Liste über den berechtigungsfreien Port IDocumentContentPort.DescribeManyAsync ermittelt.
Ablage (RAL-QM-HP-19) Der verfahrensunabhängige Dateibereich des Produkts: Ordner und Dateien, die keinem Verfahren gehören. Gegliedert in Ablagebereiche, Ordner bis 16 Ebenen tief, Namen bis 200 Zeichen, dahinter der zweistufige Papierkorb. Eigener Feature-Slice Filing mit RALQMFilingAreas, RALQMFilingEntries, RALQMFilingHistory und RALQMFilingHearingUsages; Dateimetadaten und Inhalte bleiben bei ABP File Management hinter dem neutralen IDocumentFilingPort — Filing speichert Bereichseigentum, Elementzuordnung, Papierkorbzustand und Fachhistorie. Bedient unter /filing; nutzbar nur durch aktive interne Konten mit genau einer Produktrolle Referent oder Administrator. Institutionskonten haben keine Ablage. Siehe Bausteinsicht §5.2.5.
Ablagebereich (RAL-QM-HP-19) Die Wurzel einer Ablage (FilingAreaKind): der gemeinsame Bereich, den alle berechtigten internen Konten zusammen nutzen, und je Konto ein persönlicher Bereich. Ein fremder persönlicher Bereich bleibt bei jeder Operation verborgen — auch für Administratoren und auch im Verwendungsnachweis. Jeder Bereich trägt eine Revision, die als Sperre jeder Mutation zuerst geschrieben wird. Der persönliche Bereich eines deaktivierten internen Kontos wird unter /administration/ablage-uebertragen von einem aktuellen Produktadministrator an einen aktiven Referenten übertragen; die Übertragung konsumiert nur Konten- und Bereichsprojektionen und vermittelt keinen Inhaltszugriff.
Papierkorb (Ablage) (RAL-QM-HP-19) Der zweistufige Löschweg der Ablage (FilingEntryState). „Entfernen“ setzt den Unterbaum auf Trashed und erhält die Bytes; das ausdrücklich bestätigte „Endgültig entfernen“ reserviert zuerst alle Datei-Descriptoren und persistiert Purging, danach entfernt ein ABP Background Worker nach erneuter Rechte- und Eigentumsprüfung die Inhalte und schreibt Purged fest. Aktive CAF-Freigaben und Verfahrensreferenzen auf denselben Descriptor blockieren den Auftrag (PurgeProtected); eine Übernahme blockiert ihn nicht, weil das Verfahren eine eigene Fassung besitzt. Die Ansicht /filing/trash führt Ablage- und Verfahrenszeilen zusammen — entfernte Verfahrensdateien liefert der Hearings-Port zu, ihre Wiederherstellung und ihr Löschauftrag bleiben bei Hearings. Das Leeren des Papierkorbs und eine automatische Aufbewahrungsfrist sind eigene Stories (RAL-QM-HP-64, RAL-QM-HP-65).
Übernahme (Ablagedatei ins Verfahren) (RAL-QM-HP-16) Das Einbringen einer aktiven Ablagedatei in ein Verfahren als eigenständige Verfahrensfassung: eigene DocumentId, eigener Descriptor, eigene Bytes, Herkunft Archive und der Übernahmezeitpunkt als Anlagezeitpunkt dieser Fassung. Es bleibt kein Verweis auf das Ablageoriginal: Umbenennen, Verschieben, Ersetzen, Papierkorb und endgültiges Entfernen der Quelle lassen Inhalt, Anzeigename und Berechtigungen der Verfahrensfassung unverändert. Zulässig nur aus dem gemeinsamen oder dem eigenen persönlichen Ablagebereich, nur für Dateien (nie Ordner) und nur bei offenem Fenster des gewählten Verwendungszwecks; ein abgeschlossenes (Completed) oder abgebrochenes (Cancelled) Verfahren nimmt nichts mehr auf. Ein Aufruf je Datei mit eigener Operation-ID — eine fachlich abgewiesene Datei stoppt die übrigen nicht. Bedienung über „Aus der Ablage übernehmen“ im Reiter „Dokumente“ der Verfahrensakte; Ablauf in Laufzeitsicht §6.6. Nicht zu verwechseln mit der Gegenrichtung aus RAL-QM-HP-19, der „Kopie in Ablage wiederherstellen“ einer zurückgezogenen Verfahrensunterlage.
Verwendungsnachweis (RAL-QM-HP-16) Die Auskunft, in welche Verfahren eine Ablagedatei übernommen wurde: je Übernahme das Verfahren mit Referenznummer, Bezeichnung, Status, Übernahmezeitpunkt, dem abgeleiteten Merkmal archiviert (Completed oder Cancelled) und der Angabe, ob die Verfahrensfassung dort noch vorhanden ist. Persistiert als Aggregat FilingHearingUsage (RALQMFilingHearingUsages) mit Quell-Element, gelesener Quellfassung, Verfahren, Verfahrensdokument, Zeitpunkt, Actor und Operation-ID; Zeilen werden nie gelöscht, der Nachweis bleibt deshalb auch nach endgültigem Entfernen der Quellbytes auflösbar. Er lässt Verfahren ohne Leserecht des Aufrufers und Elemente fremder persönlicher Bereiche weg, statt zu scheitern, und zeigt für eine Datei ohne Übernahme einen erläuternden Hinweis statt einer leeren Liste. Er ist zugleich die Datenquelle des Warnhinweises vor dem Entfernen und dem endgültigen Entfernen — ein Hinweis, keine Sperre.
Überfällig (RAL-QM-HP-2, RAL-QM-HP-1) Kennzeichen eines Verfahrens in der Verfahrensübersicht und Ordnungsmerkmal des Arbeitsvorrats: versandt, die maßgebliche Rückmeldefrist (zweite, sonst erste) ist nach der Kalendertagsregel des Antwortfensters abgelaufen (der Fristtag selbst zählt noch nicht), und mindestens ein aktiver Teilnehmer hat noch nicht geantwortet. Eine abgelaufene Frist allein ist nicht „überfällig“ — sind alle Stellungnahmen da oder läuft schon die Bescheidung, gibt es nichts mehr zu erwarten. Die maßgebliche Regel steht an einer Stelle (HearingOverdueRule im Hearings-Contract) und wird serverseitig ins Listen-DTO geschrieben (IsOverdue); Liste, CSV-Export und Oberfläche leiten es nie selbst ab. Seit RAL-QM-HP-1 gibt es sie in einer zweiten Fassung als Datenbankabfrage (HearingOverdueQuery in der Hearings-Runtime), weil der Arbeitsvorrat des Referenten seine überfälligen Verfahren nach vorn stellt und erst danach blättert, was eine Entscheidung je geladener Zeile nicht kann; ausgewählt wird nach ihr nicht mehr, der Referent sieht nur eigene Verfahren. HearingWorkQueueSqlServerTests hält beide Fassungen auf echtem SQL Server zusammen, auch an der Tagesgrenze. Nicht zu verwechseln mit der Ampel des Dashboards, die am Zeitstempel der Frist schaltet.
Arbeitsvorrat (RAL-QM-HP-1) Die persönliche Aufstellung offener Aufgaben auf der Startseite. Für den Referenten genau seine laufenden Verfahren — laufend heißt hier weder abgeschlossen noch storniert (SPEC-RAL-QM-HP-1 E8), enger als der Sichtbereich „Laufende“ der Verfahrensübersicht unten, der stornierte Verfahren bis zur Archivierung zeigt; überfällige eigene sind gekennzeichnet und stehen vorn (E9, E10). Für die Geschäftsführung genau die zur Freigabe eingereichten Verfahren, eigene Einreichungen eingeschlossen. Fremde und nicht zugewiesene Verfahren erscheinen in keinem Arbeitsvorrat des Referenten.
Sichtbereich (Verfahrensliste) (RAL-QM-HP-2) Welche Verfahren eine Listenabfrage überhaupt betrachtet (HearingListScope): Laufende (Standard der Verfahrensübersicht: alles außer abgeschlossen; abgebrochene bleiben sichtbar, bis ein Archiv-Zustand aus RAL-QM-HP-44 sie ausblendet), Alle (Kopiervorlagen beim Anlegen, Eintrag „Alle Status“ im Statusfilter) und Abgeschlossene (das Archiv: abgeschlossen oder abgebrochen). Ein explizit gewählter Status hat Vorrang vor dem Sichtbereich — so holt „Abgeschlossen“ im Statusfilter die ausgeblendeten Verfahren ohne zweites Bedienelement zurück.
Institutionskonto Das Zugangs-Modell für externe Teilnehmer: RAL legt je Institution dedizierte Benutzerkonten an — mehrere pro Institution, je Kontakt eines (Keycloak-Konto, über den stabilen sub gebunden); keine Selbstregistrierung, Entzug wirkt sofort und ohne Rückfallweg. Einstieg ist eine Seite mit zwei Routen: /portal/zugang-uebernehmen („Zugang übernehmen“) und /portal/teilnahme-beanspruchen („Teilnahme beanspruchen“, die derzeit einzige in der Anwendung verlinkte); jedes Konto sieht nur die eigene Institution und die eigenen Teilnahmen (US-22 ff.).
Kontogebundene Teilnahme Die konkrete Bindung einer Verfahrens-Einladung an ein Benutzerkonto des Institutionskontos (sub-korreliert); technische Grundlage des Portalzugangs.
Gastzugang (stillgelegt) Ehemaliger anonymer Einladungszugang. Seit RAL-QM-HP-31 ohne Zugriffsrecht; historische Daten bleiben erhalten, alte Einstiege führen neutral zum Login.

Technische Begriffe

Begriff Definition
RALQM Verbindlicher Präfix für alle anwendungsspezifisch persistierten Identifikatoren der HearingPortal-Anwendung: Tabellen (RALQMHearings, RALQMInstitutions, …), Error-Code-Namespace (RALQM.<Feature>:), Permission-Gruppe RALQM und Rollen RALQMAdministrator/RALQMCaseOfficer/RALQMManagement. Technische, lowercase Slugs (z. B. ral-qualitymarks) bleiben davon unabhängig. Siehe Randbedingungen.
CAF Präfix der wiederverwendbaren, anwendungsneutralen Framework-Bausteine (CAF.Modules.Authentication, CAF.Modules.Documents[.Contracts], CAF.Modules.UI.Blazor). Kein CAF.*-Projekt darf ein HearingPortal.*-Projekt oder GZ-Fachwissen referenzieren.
No-Layer (app-nolayers) Architekturstil dieses Monorepos: Domain, Application und Persistence bleiben logische Schichten, werden aber pro Modul in einer kompakten Runtime-Assembly gebündelt statt in sieben ABP-Standardprojekten. Eine weitere Assembly (z. B. eigenes .Blazor-Projekt) entsteht erst bei echtem Bedarf (unabhängige Paketierung, austauschbarer Provider, gemessene Build-Isolation). Siehe Bausteinsicht.
ABP (ABP Framework) Das zugrunde liegende .NET-Applikationsframework (Module, Application Services, dynamische Proxies für Authorization/Unit-of-Work/Audit/Validation, Identity, Permission-, Setting-, Audit-, Text-Template- und File-Management-Module). Der Host lädt ABP-Module als Composition Root; Fachcode nutzt vorhandene ABP-Bordmittel, bevor neue Infrastruktur entsteht.
Cohesion Die verbindliche Syncfusion-basierte UI-Komponentenbibliothek für alle eigenen CAF-/GZ-Fachseiten (aus dem GitLab-Gruppen-Feed, zentrale Floating-Version 3.*-*). MudBlazor/Syncfusion/Blazorise dürfen von Fachcode nicht direkt gerendert werden; ausgenommen sind allein die MVC-Razor-Pages unter /Account/**. Registrierung, Lizenzprüfung und Assets liegen in CAF.Modules.UI.Blazor.
Feature-Slice Die Standardform eines Fachmoduls: ein schmales .Contracts-Projekt (DTOs, AppService-Interfaces, IDs, Permissions, Localization) plus eine Runtime (Domain, Application, EF Core) und optional eine .Blazor-Razor-Class-Library für Feature-Seiten. Vertikal nach Capability geschnitten (Authorization, Institutions, Hearings), nicht horizontal nach technischer Schicht.
Modulgrenze Die durch Architekturtests erzwungene Compile-Time-Grenze zwischen Modulen: Consumer verwenden nur Contracts, nie eine fremde Runtime, Entity, Tabelle oder Repository; CAF.Modules.* → HearingPortal.* und Cross-Module-EF-Navigation/-Foreign-Keys sind verboten. Über Modulgrenzen werden ausschließlich IDs referenziert. Siehe Bausteinsicht, Querschnittliche Konzepte.
VERTRAG.md Die verbindliche, deutschsprachige Vertragsdatei je CAF.Modules-Baustein (Wurzelverzeichnis des Projekts, z. B. modules/Authentication/src/VERTRAG.md). Benennt zugesicherte öffentliche Fläche, ausdrückliche Nicht-Zusicherungen, Versionierungsregel und Feed-Voraussetzungen. Ein Quelltext-Vertrag, kein Paket-Readme: die Bausteine sind bewusst nicht packbar. FrameworkPackagingTests verlangt Existenz, nicht-leeren Inhalt und beide Pflichtabschnitte. Jede Änderung an der zugesicherten Fläche aktualisiert VERTRAG.md im selben Änderungssatz.
Seed Der initiale/aktualisierende Datenbestand, der beim Migrations-/Startvorgang eingespielt wird (z. B. Rollen-Grants nach neuen Feature-Permissions). Läuft über HearingPortalDbMigrationService im Host; erfordert nach Rechteänderungen einen erneuten Seed-Lauf, aber keine Schemamigration.
Data-preserving Migration Eine EF-Core-Migration, die Strukturänderungen (z. B. Umbenennung von Tabellen/Spalten beim RALQM-Rename) ohne Datenverlust umsetzt, typischerweise über RenameTable/RenameColumn statt Drop/Create. Historische Migrationen behalten dabei ihre alten Identifiers; nur neue Migrationen verwenden die aktuelle Namenskonvention. Pflichtnachweis: realer Round-Trip auf leerer SQL-Server-Datenbank plus has-pending-model-changes grün.
Aggregate Ein fachliches Objekt, das seine Invarianten selbst über benannte Operationen (z. B. Submit, Approve, Cancel) schützt, keine öffentlichen Setter auf invarianten-relevanten Properties besitzt und optimistische Concurrency trägt. Beispiel: die Anhörung mit Teilnehmern, Dokumenten und Freigaben.
Application Service Der Einstiegspunkt eines Anwendungsfalls: prüft Permission und Objektzugriff, lädt das Aggregate, ruft eine benannte Domänenoperation auf und persistiert innerhalb der ABP Unit of Work. Wird nicht sealed, damit ABP die nötige dynamische Proxy-Interception anwenden kann.
Contracts Das schmale, öffentliche Projekt eines Moduls: DTOs, AppService-Interfaces, stabile IDs, Permission-Definitionen und DE/EN-Localization. Enthält keine EF-Typen, Entities, Repositories oder Implementierungen.
Host (Composition Root) Das einzige startbare Projekt (HearingPortal/Host), das Module lädt, Middleware/Routing verkabelt, den gemeinsamen HearingPortalDbContext besitzt und Migrationen erzeugt/ausführt. Enthält bewusst keine neue Fachlogik oder Fachentities.
Interactive Server Der von der Blazor-Web-App gewählte Render-Modus (kein WebAssembly-Client). Feature-Komponenten bleiben render-mode-neutral und setzen kein @rendermode; der Host entscheidet zentral.
BrowserGate Der separate, exklusive End-to-End-Nachweis über einen echten Browser (Login, Hauptprozesse, Permission-Denial, Assets, Responsive-Verhalten, axe/WCAG). Läuft als Konsolenprogramm ohne Test-SDK, ist Teil der Solution, aber nicht Teil des normalen dotnet test-Laufs.
Quality Gate Zentraler Selektor tools/scripts/Invoke-QualityGate.ps1: bestimmt aus vollständigen Ref-Diffs und dem alten/neuen Projektgraphen betroffene Apps. Jede ausgewählte ausführbare App durchläuft ihre Pflichtschritte; Browserstrecke, axe, Tastatur/Fokus und Browser-Berechtigungsprüfungen sind optional und standardmäßig aus. Reine Doku prüft Links und Reporting; -All wählt CAF und alle Apps ohne optionale Gruppen einzuschalten. Fehlende/übersprungene Pflichtschritte und Nachweise für einen anderen Tipp blockieren den Push. Details im Entwicklungsleitfaden.