diff --git a/AI-README.md b/AI-README.md
index 6b63880..37f581b 100644
--- a/AI-README.md
+++ b/AI-README.md
@@ -2,7 +2,7 @@
## Projekt und Sicherheitsziel
-Das Repository enthält ein .NET-Framework-4.6.1-WinForms-Tool für kontrollierte BizTalk-2020-Wartungsoperationen. Änderungen müssen Dry-run, explizite Freigabe realer Aktionen, sichere Reihenfolgen und wiederherstellbare Installergrenzen erhalten. Die aktuelle Produktversion ist 2.3.3.
+Das Repository enthält ein .NET-Framework-4.6.1-WinForms-Tool für kontrollierte BizTalk-2020-Wartungsoperationen. Änderungen müssen Dry-run, explizite Freigabe realer Aktionen, sichere Reihenfolgen und wiederherstellbare Installergrenzen erhalten. Die aktuelle Produktversion ist 2.3.4.
## Installerinvarianten
@@ -37,6 +37,7 @@ Die zentrale Implementierung liegt in `src/BizTalkPlatformManagementTool.Setup/I
- Emergency Restore überschreibt niemals die Eingabe-`before.json`, erzeugt eine timestamp-basierte Kopie und stellt `ENTSSO` vor Host Instances sicher.
- Emergency Restore muss mit genau einer validen `before.json` funktionieren; Dateien eines vorherigen fehlgeschlagenen Laufs dürfen keine Voraussetzung sein.
- Ein älterer kompatibler Snapshot, insbesondere aus 2.1.3, darf nicht allein anhand seines `ToolVersion`-Werts abgelehnt werden.
+- Alle laufzeitrelevanten Bestätigungsdialoge müssen mit dem Hauptformular als Owner angezeigt werden. Vor dem echten Drain-Checkpoint muss der Status sichtbar `ACTION REQUIRED`/pausiert melden; Dry-run muss ausdrücklich erklären, dass keine Runtime-Änderung und keine echte Drain-Entscheidung erfolgt.
- Der echte Emergency Restore erzeugt nach Möglichkeit automatisch einen timestamp-basierten Soll/Ist-Diff aus Recovery-Quelle und Nachher-Snapshot.
- Die zentrale best-effort Orchestrierung liegt in `OperationPlanExecutor`; die WMI-/Service-Zustandsprüfung bleibt im produktiven Runtime-Adapter.
- Datei-Logging und optionale GUI-Logweiterleitung sind Diagnosekanäle und dürfen niemals einen fachlichen Planschritt oder dessen Fehlerbehandlung unterbrechen.
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 1aa76fe..dffe93c 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,6 +1,20 @@
# Changelog
+## [2.3.4] - 2026-08-27
+### Fixed
+- Shutdown plan review, inbound-drain and emergency-restore confirmations are now owned by the main window, preventing a modal decision from being hidden behind the application in RDP and multi-monitor sessions.
+- The shutdown status now changes from a generic preparation message to an explicit `ACTION REQUIRED` / paused state before the real inbound-drain decision.
+
+### Changed
+- Dry-run and real execution are distinguished with prominent mode text, color and persistent status guidance; execution settings cannot be changed while an operation is active.
+- A shutdown Dry-run now displays an owned informational notice explaining that it changes no receive locations and therefore does not open the real drain decision dialog.
+- The prepared real-shutdown confirmation explicitly announces the second decision after the receive-location phase; closing the real checkpoint dialog is explicitly documented as a safe stop.
+
+### Added
+- Regression coverage for checkpoint creation when later shutdown work exists without enabled receive locations, and for distinct Dry-run, prepared-plan and paused-checkpoint operator guidance.
+- A dedicated PROD root-cause analysis, validation runbook and plain-text handoff for shutdown-dialog visibility.
+
## [2.3.3] - 2026-08-27
### Fixed
- Snapshot, shutdown and restore rows no longer show every Send Port, Receive Location and Orchestration under `(Unknown Application)` when the BizTalk runtime WMI classes omit application ownership.
diff --git a/Installation.md b/Installation.md
index 770be90..4ef08d6 100644
--- a/Installation.md
+++ b/Installation.md
@@ -15,8 +15,8 @@ Das Release erzeugt folgende Dateien unter `artifacts`:
- `BizTalkPlatformManagementTool-Setup.zip.b64.txt`: Certutil-kompatible Base64-Übertragung
- `BizTalkPlatformManagementTool-Setup.zip.sha256.txt`: SHA-256 des ZIP-Archivs
- `BizTalkPlatformManagementTool-Setup.zip`: direkt entpackbares Installationspaket
-- `BizTalkPlatformManagementTool-2.3.3-source.zip.b64.txt`: vollständiger Quellstand als Base64-TXT
-- `BizTalkPlatformManagementTool-2.3.3-source.zip.sha256.txt`: SHA-256 des Quellarchivs
+- `BizTalkPlatformManagementTool-2.3.4-source.zip.b64.txt`: vollständiger Quellstand als Base64-TXT
+- `BizTalkPlatformManagementTool-2.3.4-source.zip.sha256.txt`: SHA-256 des Quellarchivs
Auf dem Zielsystem wird die TXT-Datei so rekonstruiert und geprüft:
diff --git a/PROD-ScheduledTask-und-Laufzeitlogging-2026-08-26.txt b/PROD-ScheduledTask-und-Laufzeitlogging-2026-08-26.txt
index 434b1ad..c0a739e 100644
--- a/PROD-ScheduledTask-und-Laufzeitlogging-2026-08-26.txt
+++ b/PROD-ScheduledTask-und-Laufzeitlogging-2026-08-26.txt
@@ -1,6 +1,6 @@
PROD-RUNBOOK: SCHEDULEDTASK-STEUERUNG UND PERSISTENTES LAUFZEITLOGGING
Stand: 2026-08-26
-Zielversion: 2.3.3
+Zielversion: 2.3.4
Adapter: BizTalk ScheduledTask Adapter 7.0.2
Adapterpfad: C:\Program Files (x86)\BizTalk ScheduledTask Adapter 7.0.2
@@ -13,8 +13,8 @@ laufenden Toolprozess. Das Tool kopiert keine DLL und aendert den GAC nicht.
VORBEREITUNG
1. before.json, Reports und vorhandene Logs extern sichern.
-2. Setup 2.3.3 als Administrator installieren/aktualisieren.
-3. setup_version=2.3.3.0 und erfolgreichen Ziel-Self-Test pruefen.
+2. Setup 2.3.4 als Administrator installieren/aktualisieren.
+3. setup_version=2.3.4.0 und erfolgreichen Ziel-Self-Test pruefen.
4. Tool als Administrator starten, gruenen Startup-Log-Pruefeintrag kontrollieren,
dann Log Folder oeffnen. Normalfall: %ProgramData%\BizTalkPlatformManagementTool\Logs.
5. Im lokalen BizTalk-Ordner Microsoft.BizTalk.Scheduler.dll pruefen.
@@ -74,7 +74,7 @@ ABNAHME
- Teilfehler bleibt Failed, spaetere unabhaengige Schritte laufen weiter.
- Historie erscheint nach Neustart im Grid.
- Vortage komprimiert, 30 Kalendertage aufbewahrt.
-- Setup/Tool/Reports zeigen 2.3.3 bzw. 2.3.3-net461.
+- Setup/Tool/Reports zeigen 2.3.4 bzw. 2.3.4-net461.
Detailfassung:
docs\PROD-ScheduledTask-und-Laufzeitlogging-2026-08-26.md
diff --git a/PROD-Shutdown-Dialog-Sichtbarkeit-2.3.4.txt b/PROD-Shutdown-Dialog-Sichtbarkeit-2.3.4.txt
new file mode 100644
index 0000000..e879cff
--- /dev/null
+++ b/PROD-Shutdown-Dialog-Sichtbarkeit-2.3.4.txt
@@ -0,0 +1,55 @@
+PROD-HANDOFF: SHUTDOWN-DIALOG-SICHTBARKEIT - VERSION 2.3.4
+Datum: 2026-08-27
+
+BEFUND
+Nach der Receive-Location-Phase wurde kein sichtbarer Dialog zur Bestaetigung
+des Leerlaufens wahrgenommen.
+
+URSACHEN
+1. Dry run ist standardmaessig aktiv. Er deaktiviert keine Receive Location und
+ ruft deshalb bewusst keinen echten Drain-Entscheidungsdialog auf. Dieser
+ Unterschied war waehrend der Ausfuehrung nicht deutlich genug sichtbar.
+2. Die bisherigen MessageBoxen hatten keinen Owner. In RDP-/Multi-Monitor-
+ Sitzungen konnte ein modaler Plan- oder Drain-Dialog hinter dem Hauptfenster
+ liegen, waehrend dort nur "Preparing shutdown..." sichtbar blieb.
+
+SICHERHEIT
+Der persistierte OperatorCheckpoint war vorhanden. Ohne Dialog-Callback, bei
+Fehler, Nein oder Schliessen stoppt der echte Shutdown fail-closed. Spaetere
+Orchestrierungen, Send Ports und Host Instances bleiben NotExecuted.
+
+FIX 2.3.4
+- Alle runtime-relevanten Bestaetigungen sind an das Hauptfenster gebunden.
+- Am echten Checkpoint steht sichtbar ACTION REQUIRED / Shutdown paused.
+- Der erste Realbetrieb-Dialog kuendigt die zweite Drain-Entscheidung an.
+- Dry run und REAL EXECUTION sind farblich und textlich eindeutig.
+- Dry run zeigt einen eigenen Hinweis: keine Mutation, keine echte Drain-
+ Entscheidung.
+- Einstellungen sind waehrend einer laufenden Operation gesperrt.
+
+PROD-ABNAHME
+1. Setup 2.3.4 installieren; setup_version=2.3.4.0 und Ziel-Self-Test pruefen.
+2. Dry run: Shutdown Dry Run - No Runtime Changes muss sichtbar sein; keine
+ BizTalk-Mutation.
+3. shutdown-plan.json: OperatorCheckpoint muss vor jeder spaeteren Shutdown-
+ Phase stehen.
+4. Freigegebenes Wartungsfenster: Dry run deaktivieren; REAL EXECUTION muss
+ sichtbar sein.
+5. Ersten gespeicherten Plan bestaetigen.
+6. Nach Receive Locations: ACTION REQUIRED und owner-gebundener Drain-Dialog.
+7. Zuerst Nein testen: CheckpointDecision=Stop, alle spaeteren Zeilen
+ NotExecuted.
+8. Danach kontrolliert wiederholen; Group Hub und Monitoring pruefen; erst bei
+ bestaetigtem Leerlauf Ja auswaehlen.
+9. RDP minimieren/wiederherstellen und ggf. Monitorwechsel testen.
+
+ALTE LAEUFE UNTERSCHEIDEN
+- DryRun=true + keine CheckpointDecision: erwartete Simulation.
+- Kein Ergebnis + UI bleibt Preparing shutdown: moeglicherweise verdeckter
+ Dialog; Alt+Tab pruefen, Prozess nicht blind beenden.
+- CheckpointDecision=Stop/Error: sicher vor spaeteren Phasen gestoppt.
+- CheckpointDecision=Continue: positive Bedienerentscheidung wurde erfasst.
+
+Portable Verifikation: Release-Build aller vier Projekte, 43 Regressionstests,
+Runtime-Self-Test und manifest-/hashgepruefte Paketierung. Die finale Dialog-
+und BizTalk-Pruefung erfolgt auf Windows/PROD.
diff --git a/README.md b/README.md
index 0426264..fed53f1 100644
--- a/README.md
+++ b/README.md
@@ -29,6 +29,7 @@ WinForms tool for controlled Microsoft BizTalk Server 2020 platform operations d
- Thirty-day runtime-log retention with GZip compression for completed daily logs
- Process-local ScheduledTask adapter dependency resolution for `scheduler:` receive locations without modifying the GAC
- Fail-closed operator drain checkpoint after all receive locations and before later shutdown phases
+- Owner-bound shutdown confirmations with an explicit paused/action-required status and an unambiguous Dry-run notice
- Best-effort plan execution: one isolated WMI failure is recorded while remaining independent steps continue
- Idempotent execution that skips artifacts already in the requested target state
- Durable per-step result reports even when a shutdown or restore completes only partially
@@ -78,7 +79,7 @@ Restore:
Orchestrations that were `Bound` are deliberately left unchanged during restore to avoid accidentally making them `Unbound`.
-The drain checkpoint is part of `shutdown-plan.json`. In a real run, **Yes** records `CheckpointDecision=Continue`; **No**, a closed dialog or a callback error fails closed before orchestrations, send ports and host instances. All later rows are persisted as `NotExecuted`, followed by a partial-state snapshot and `shutdown-result.json`. Dry-run shows the checkpoint but never opens a decision dialog.
+The drain checkpoint is part of `shutdown-plan.json`. In a real run, the status changes to **ACTION REQUIRED**, and the owned dialog remains in front of the main window. **Yes** records `CheckpointDecision=Continue`; **No**, a closed dialog or a callback error fails closed before orchestrations, send ports and host instances. All later rows are persisted as `NotExecuted`, followed by a partial-state snapshot and `shutdown-result.json`. Dry-run shows the checkpoint but never opens a decision dialog; it displays a separate notice explaining that no receive location was changed.
BizTalk applications displaying `Partially Started` immediately after receive locations are disabled is expected. Microsoft defines “Partial Stop - Allow running instances to continue” as disabling only receive locations so in-flight instances can finish. The tool deliberately does not issue an additional application-level stop: its stronger variants can stop or unenlist artifacts, remove subscriptions, undeploy policies or terminate instances. The existing artifact-level plan preserves exact snapshot state and controlled global ordering.
@@ -148,5 +149,6 @@ Targeted German inline comments explain non-obvious operational decisions such a
- [PROD runtime-log storage validation and 2.3.1 fix](docs/PROD-Laufzeitlog-Ablage-2026-08-26.md)
- [PROD shutdown drain checkpoint and application-status analysis](docs/PROD-Shutdown-Drain-Checkpoint-2026-08-26.md)
- [PROD application-association analysis and 2.3.3 validation](docs/PROD-Application-Zuordnung-2026-08-27.md)
+- [PROD shutdown-dialog visibility analysis and 2.3.4 validation](docs/PROD-Shutdown-Dialog-Sichtbarkeit-2026-08-27.md)
- [AI maintainer handoff](AI-README.md)
- [References](REFERENCES.md)
diff --git a/docs/PROD-ScheduledTask-und-Laufzeitlogging-2026-08-26.md b/docs/PROD-ScheduledTask-und-Laufzeitlogging-2026-08-26.md
index c864b3d..935910b 100644
--- a/docs/PROD-ScheduledTask-und-Laufzeitlogging-2026-08-26.md
+++ b/docs/PROD-ScheduledTask-und-Laufzeitlogging-2026-08-26.md
@@ -2,7 +2,7 @@
**Stand:** 2026-08-26
-**Zielversion:** 2.3.3
+**Zielversion:** 2.3.4
**Betroffener Adapter:** BizTalk ScheduledTask Adapter 7.0.2
@@ -17,8 +17,8 @@ Version 2.3.1 erkennt ScheduledTask-Schritte an Adaptername oder `scheduler:`-UR
## Vorbereitung
1. Vorhandene `before.json`, Ergebnisdateien und Laufzeitlogs außerhalb des Installationsordners sichern.
-2. Setup 2.3.3 als Administrator installieren beziehungsweise aktualisieren.
-3. Im Setup-Log `setup_version=2.3.3.0` und erfolgreichen Ziel-Self-Test prüfen.
+2. Setup 2.3.4 als Administrator installieren beziehungsweise aktualisieren.
+3. Im Setup-Log `setup_version=2.3.4.0` und erfolgreichen Ziel-Self-Test prüfen.
4. Tool als Administrator starten, den grünen Eintrag `Runtime log storage verified by startup append` prüfen und mit **Log Folder** den dort genannten aktiven Pfad öffnen. Normalfall ist `%ProgramData%\BizTalkPlatformManagementTool\Logs`.
5. Prüfen, dass der lokale BizTalk-Installationsordner `Microsoft.BizTalk.Scheduler.dll` enthält.
6. Nur wenn BizTalk oder der Adapter abweichend installiert wurde: `AdapterAssemblySearchPaths` in `BizTalkPlatformManagementTool.exe.config` um den vorhandenen lokalen Ordner ergänzen. Mehrere Pfade werden mit Semikolon getrennt. Keine DLL aus ACC, einer alten BizTalk-Version oder einem Downloadordner kopieren.
@@ -78,6 +78,6 @@ Der Fehlerdatensatz enthält Exceptiontyp, HRESULT, innere Ausnahmen, vorhandene
- Ergebnisreport und Nachher-Snapshot werden auch bei einem Teilfehler soweit möglich geschrieben.
- Historisches Log erscheint nach Neustart wieder im Grid.
- Vortagslogs werden komprimiert und exakt 30 Kalendertage aufbewahrt.
-- Setup, Tool und Ergebnisdateien melden Version 2.3.3 beziehungsweise `2.3.3-net461`.
+- Setup, Tool und Ergebnisdateien melden Version 2.3.4 beziehungsweise `2.3.4-net461`.
Die lokale Mono-Toolchain prüft Resolverlogik, Identitätsgrenze, Persistenz, Kompression und Aufbewahrung ohne BizTalk. Die endgültige Freigabe erfordert diesen realen Windows-/BizTalk-/PROD-Test.
diff --git a/docs/PROD-Shutdown-Dialog-Sichtbarkeit-2026-08-27.md b/docs/PROD-Shutdown-Dialog-Sichtbarkeit-2026-08-27.md
new file mode 100644
index 0000000..4909ae3
--- /dev/null
+++ b/docs/PROD-Shutdown-Dialog-Sichtbarkeit-2026-08-27.md
@@ -0,0 +1,57 @@
+# PROD-Analyse: Shutdown-Dialog nicht sichtbar
+
+**Datum:** 27.08.2026
+
+**Zielversion:** 2.3.4
+
+**Befund:** Nach der Receive-Location-Phase wurde kein sichtbarer Dialog zur Bestätigung des Leerlaufens wahrgenommen.
+
+## Ergebnis der Ursachenanalyse
+
+Der fachliche Checkpoint war in Version 2.3.3 vorhanden: Jeder Shutdown-Plan mit nachgelagerter Arbeit enthält genau eine persistierte `OperatorCheckpoint`-Zeile vor Orchestrierungen, Send Ports und lokalen Host Instances. Ohne Callback, bei einem Fehler oder bei **Nein** stoppt der Executor fail-closed; keine geschützte spätere Phase wird ausgeführt.
+
+Die Oberfläche hatte aber zwei voneinander unabhängige Sichtbarkeitsprobleme:
+
+1. **Dry run ist standardmäßig aktiv.** In diesem Modus wird keine Receive Location deaktiviert. Deshalb wird der echte Entscheidungsdialog absichtlich nicht aufgerufen; nur die Checkpoint-Zeile wird simuliert. Der kleine Haken und der generische Status machten diesen Unterschied während der Ausführung nicht eindeutig.
+2. **Die Bestätigungen hatten keinen Fenster-Owner.** `MessageBox.Show(...)` wurde aus dem UI-Thread aufgerufen, aber nicht an das Hauptformular gebunden. Unter RDP, nach Monitorwechseln oder bei abweichender Z-Order konnte der modale Dialog hinter dem Hauptfenster beziehungsweise außerhalb des erwarteten Vordergrunds liegen. Der Background-Worker wartete dann korrekt auf `Invoke`, während das Hauptfenster weiterhin nur `Preparing shutdown...` zeigte und deaktiviert wirkte.
+
+Der Screenshot allein unterscheidet die beiden Fälle nicht, weil der Einstellungsbereich mit **Dry run** nicht sichtbar und kein `shutdown-result.json` beigefügt ist. Der sichtbare Zustand `Preparing shutdown...` bei bereits dargestelltem Plan ist jedoch mit einem wartenden, owner-losen Plan- oder Drain-Dialog vereinbar. Ein abgeschlossenes `DryRun=true` mit leerer `CheckpointDecision` belegt dagegen die beabsichtigte Simulation.
+
+## Korrektur in 2.3.4
+
+- Planfreigabe, Drain-Entscheidung und Emergency-Restore-Bestätigung verwenden das Hauptformular explizit als Owner und holen es vor dem Anzeigen nach vorn.
+- Unmittelbar vor der echten Drain-Entscheidung meldet die Statuszeile: `ACTION REQUIRED: Shutdown paused`.
+- Der Titel des Checkpoints beginnt mit `ACTION REQUIRED`; der Text erklärt, dass der Shutdown pausiert ist und **Nein oder Schließen** sicher stoppt.
+- Die erste Freigabe eines echten Shutdowns kündigt den zweiten Dialog nach der Receive-Location-Phase ausdrücklich an.
+- Dry-run und Realbetrieb haben gut sichtbare, farblich getrennte Texte. Während einer Operation sind diese Einstellungen gesperrt.
+- Ein Shutdown-Dry-run zeigt einen eigenen owner-gebundenen Hinweis: keine Runtime-Änderung, keine deaktivierte Receive Location und daher keine echte Drain-Entscheidung.
+
+Die Sicherheitssemantik bleibt unverändert: Das Tool behauptet keinen automatisch erkannten Leerlauf. Nur der Operator kann nach Prüfung von Group Hub und Enterprise-Monitoring mit **Ja** fortsetzen.
+
+## Diagnose vorhandener 2.3.2/2.3.3-Läufe
+
+`shutdown-result.json` und `shutdown-plan.json` sichern und wie folgt bewerten:
+
+| Evidenz | Bedeutung |
+|---|---|
+| `DryRun=true`, Checkpoint-Outcome `DryRun`, keine `CheckpointDecision` | Erwartete Simulation; es gab keine Runtime-Änderung und bewusst keinen Entscheidungsdialog. |
+| Kein Ergebnisreport, UI bleibt bei `Preparing shutdown...` | Lauf wartet möglicherweise auf einen verdeckten Plan- oder Drain-Dialog; Taskwechsel/Alt+Tab prüfen. Prozess nicht blind beenden. |
+| `DryRun=false`, `CheckpointDecision=Stop` | Operator hat Nein gewählt oder sicher gestoppt; spätere Zeilen müssen `NotExecuted` sein. |
+| `DryRun=false`, `CheckpointDecision=Error` | Dialogmechanismus fehlte/fehlerhaft; fail-closed, spätere Zeilen müssen `NotExecuted` sein. |
+| `DryRun=false`, `CheckpointDecision=Continue` | Eine positive Entscheidung wurde erfasst; spätere Phasen durften laufen. |
+| Plan ohne `OperatorCheckpoint` | Nur zulässig, wenn keine Orchestrierung, kein Send Port und keine lokale Host Instance nachgelagert herunterzufahren war. |
+
+## PROD-Abnahme 2.3.4
+
+1. Setup 2.3.4 als Administrator installieren und im Setup-Log `setup_version=2.3.4.0` sowie den erfolgreichen Ziel-Self-Test prüfen.
+2. Tooldateiversion und Self-Test-Ausgabe `2.3.4-net461` prüfen.
+3. **Dry run** aktiviert lassen, **Shutdown** wählen und den Hinweis `Shutdown Dry Run - No Runtime Changes` prüfen. Es darf keine BizTalk-Mutation stattfinden.
+4. `shutdown-plan.json` prüfen: Bei späterer Shutdown-Arbeit steht `OperatorCheckpoint` vor Orchestrierungen, Send Ports und Host Instances.
+5. In einem freigegebenen Wartungsfenster **Dry run** deaktivieren. Der Modus muss `REAL EXECUTION - drain dialog required` anzeigen.
+6. Den gespeicherten Plan im ersten owner-gebundenen Dialog bestätigen.
+7. Nach den Receive Locations muss die Statuszeile `ACTION REQUIRED` zeigen und der Dialog `ACTION REQUIRED - Confirm BizTalk Inbound Drain` vor dem Hauptfenster stehen.
+8. Zuerst **Nein** testen. `shutdown-result.json` muss `CheckpointDecision=Stop` und für alle späteren Zeilen `NotExecuted` enthalten.
+9. Den kontrollierten Test wiederholen, Group Hub/Monitoring prüfen und erst nach bestätigtem Leerlauf **Ja** wählen. Danach dürfen Orchestrierungen, Send Ports und Host Instances folgen.
+10. RDP minimieren/wiederherstellen und, falls im Betrieb üblich, Monitorwechsel testen. Der owner-gebundene Dialog muss dem Hauptfenster zugeordnet bleiben.
+
+Die portable Verifikation umfasst Release-Build aller vier Projekte, 43 Regressionstests, Runtime-Self-Test und die manifest-/hashgeprüfte Paketierung. Die abschließende Sichtbarkeits- und BizTalk-Abnahme bleibt ein Windows-/PROD-Test.
diff --git a/src/BizTalkPlatformManagementTool.Setup/InstallerEngine.cs b/src/BizTalkPlatformManagementTool.Setup/InstallerEngine.cs
index c2a3b84..1ad3e71 100644
--- a/src/BizTalkPlatformManagementTool.Setup/InstallerEngine.cs
+++ b/src/BizTalkPlatformManagementTool.Setup/InstallerEngine.cs
@@ -23,7 +23,7 @@ namespace BizTalkPlatformManagementTool.Setup
private const string ProductName = "BizTalk Platform Management Tool";
/// Aktuelle Produktversion des Installers und Uninstall-Eintrags.
- private const string ProductVersion = "2.3.3";
+ private const string ProductVersion = "2.3.4";
///
/// Wartezeiten zwischen Wiederholungen atomarer Verzeichnisverschiebungen.
diff --git a/src/BizTalkPlatformManagementTool.Setup/MainForm.cs b/src/BizTalkPlatformManagementTool.Setup/MainForm.cs
index 186deb5..6b0359e 100644
--- a/src/BizTalkPlatformManagementTool.Setup/MainForm.cs
+++ b/src/BizTalkPlatformManagementTool.Setup/MainForm.cs
@@ -65,7 +65,7 @@ namespace BizTalkPlatformManagementTool.Setup
{
AutoSize = true,
Font = new Font(Font.FontFamily, 14, FontStyle.Bold),
- Text = "BizTalk Platform Management Tool 2.3.3"
+ Text = "BizTalk Platform Management Tool 2.3.4"
});
root.Controls.Add(new Label
{
diff --git a/src/BizTalkPlatformManagementTool.Setup/Properties/AssemblyInfo.cs b/src/BizTalkPlatformManagementTool.Setup/Properties/AssemblyInfo.cs
index 9308875..c5340ca 100644
--- a/src/BizTalkPlatformManagementTool.Setup/Properties/AssemblyInfo.cs
+++ b/src/BizTalkPlatformManagementTool.Setup/Properties/AssemblyInfo.cs
@@ -8,6 +8,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyProduct("BizTalk Platform Management Tool")]
[assembly: ComVisible(false)]
[assembly: Guid("675b68a9-bd80-46a5-b8c5-3b11b0b374e2")]
-[assembly: AssemblyVersion("2.3.3.0")]
-[assembly: AssemblyFileVersion("2.3.3.0")]
+[assembly: AssemblyVersion("2.3.4.0")]
+[assembly: AssemblyFileVersion("2.3.4.0")]
[assembly: InternalsVisibleTo("BizTalkPlatformManagementTool.Tests")]
diff --git a/src/BizTalkPlatformManagementTool.Setup/app.manifest b/src/BizTalkPlatformManagementTool.Setup/app.manifest
index 6fdf250..ae2fbf2 100644
--- a/src/BizTalkPlatformManagementTool.Setup/app.manifest
+++ b/src/BizTalkPlatformManagementTool.Setup/app.manifest
@@ -1,6 +1,6 @@
-
+
diff --git a/src/BizTalkPlatformManagementTool/BizTalkPlatformManagementTool.csproj b/src/BizTalkPlatformManagementTool/BizTalkPlatformManagementTool.csproj
index 84dfe17..658663d 100644
--- a/src/BizTalkPlatformManagementTool/BizTalkPlatformManagementTool.csproj
+++ b/src/BizTalkPlatformManagementTool/BizTalkPlatformManagementTool.csproj
@@ -70,6 +70,7 @@
+
diff --git a/src/BizTalkPlatformManagementTool/Properties/AssemblyInfo.cs b/src/BizTalkPlatformManagementTool/Properties/AssemblyInfo.cs
index 5a10cdf..701164a 100644
--- a/src/BizTalkPlatformManagementTool/Properties/AssemblyInfo.cs
+++ b/src/BizTalkPlatformManagementTool/Properties/AssemblyInfo.cs
@@ -9,6 +9,6 @@ using System.Runtime.InteropServices;
[assembly: AssemblyCopyright("Copyright © 2026")]
[assembly: ComVisible(false)]
[assembly: Guid("2c5b2c0a-f407-46c2-9e3b-1fa09fa8445a")]
-[assembly: AssemblyVersion("2.3.3.0")]
-[assembly: AssemblyFileVersion("2.3.3.0")]
+[assembly: AssemblyVersion("2.3.4.0")]
+[assembly: AssemblyFileVersion("2.3.4.0")]
[assembly: InternalsVisibleTo("BizTalkPlatformManagementTool.Tests")]
diff --git a/src/BizTalkPlatformManagementTool/Services/BizTalkOperationService.cs b/src/BizTalkPlatformManagementTool/Services/BizTalkOperationService.cs
index e3d7651..dcf758c 100644
--- a/src/BizTalkPlatformManagementTool/Services/BizTalkOperationService.cs
+++ b/src/BizTalkPlatformManagementTool/Services/BizTalkOperationService.cs
@@ -16,7 +16,7 @@ namespace BizTalkPlatformManagementTool.Services
///
/// Current tool version written into generated snapshots.
///
- public const string Version = "2.3.3-net461";
+ public const string Version = "2.3.4-net461";
///
/// Fallback application name used when neither WMI nor the ExplorerOM catalog can resolve ownership.
diff --git a/src/BizTalkPlatformManagementTool/Ui/MainForm.cs b/src/BizTalkPlatformManagementTool/Ui/MainForm.cs
index dc2768d..7d172eb 100644
--- a/src/BizTalkPlatformManagementTool/Ui/MainForm.cs
+++ b/src/BizTalkPlatformManagementTool/Ui/MainForm.cs
@@ -238,6 +238,8 @@ namespace BizTalkPlatformManagementTool.Ui
_dryRunCheckBox.Dock = DockStyle.Fill;
_dryRunCheckBox.Margin = new Padding(8, 7, 8, 7);
_dryRunCheckBox.TextAlign = ContentAlignment.MiddleLeft;
+ _dryRunCheckBox.CheckedChanged += DryRunCheckBoxCheckedChanged;
+ UpdateDryRunPresentation();
var browseButton = new Button { Text = "Browse...", Dock = DockStyle.Fill, Margin = new Padding(8, 5, 8, 5) };
browseButton.Click += BrowseButtonClick;
@@ -404,7 +406,9 @@ namespace BizTalkPlatformManagementTool.Ui
private void ShutdownClick(object sender, EventArgs e)
{
var options = GetOptions();
- RunAsync("Preparing shutdown...", () =>
+ RunAsync(options.DryRun
+ ? "DRY RUN: Preparing shutdown plan; no runtime state will be changed."
+ : "REAL SHUTDOWN: Preparing snapshot and saved plan.", () =>
{
var snapshot = _service.CreateSnapshot(options.Server);
_service.SaveSnapshot(options.OutputDirectory, "before.json", snapshot);
@@ -412,7 +416,11 @@ namespace BizTalkPlatformManagementTool.Ui
// Der exakte, frisch erzeugte Plan wird vor Bestätigung und jeder Laufzeitänderung gespeichert.
var planPath = _service.SavePlan(options.OutputDirectory, "shutdown-plan.json", plan);
ShowPlan(plan);
- if (!options.DryRun && !ConfirmPreparedPlan("Shutdown", plan, options.Server, planPath))
+ if (options.DryRun)
+ {
+ ShowShutdownDryRunNotice(plan, planPath);
+ }
+ else if (!ConfirmPreparedPlan("Shutdown", plan, options.Server, planPath))
{
_logger.Warning("Shutdown cancelled after plan review. No runtime state was changed.");
return;
@@ -706,13 +714,13 @@ namespace BizTalkPlatformManagementTool.Ui
var confirmed = false;
Action showConfirmation = () =>
{
- var executableSteps = plan.Steps.Count(x => x.Execute && !string.Equals(x.Kind, OperationStepKind.OperatorCheckpoint.ToString(), StringComparison.OrdinalIgnoreCase));
- var checkpointCount = plan.Steps.Count(x => x.Execute && string.Equals(x.Kind, OperationStepKind.OperatorCheckpoint.ToString(), StringComparison.OrdinalIgnoreCase));
+ _statusLabel.Text = "ACTION REQUIRED: Review the saved " + actionName.ToLowerInvariant() + " plan and answer the owned confirmation dialog.";
+ Activate();
+ BringToFront();
var result = MessageBox.Show(
- actionName + " will execute " + executableSteps + " step(s) on server '" + server + "'.\n\n"
- + (checkpointCount == 0 ? string.Empty : "The saved plan contains an operator drain checkpoint after all receive locations.\n\n")
- + "The exact plan was saved to:\n" + planPath + "\n\nContinue now?",
- "Confirm Prepared BizTalk Plan",
+ this,
+ ShutdownOperatorPrompts.BuildPreparedPlanConfirmation(actionName, plan, server, planPath),
+ ShutdownOperatorPrompts.PreparedPlanTitle,
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning,
MessageBoxDefaultButton.Button2);
@@ -730,6 +738,37 @@ namespace BizTalkPlatformManagementTool.Ui
return confirmed;
}
+ ///
+ /// Makes the dry-run boundary explicit: the prepared checkpoint is visible in
+ /// the plan, but no receive location changes and no real drain decision occurs.
+ ///
+ /// The prepared shutdown plan.
+ /// The durable path of the prepared plan.
+ private void ShowShutdownDryRunNotice(OperationPlan plan, string planPath)
+ {
+ Action showNotice = () =>
+ {
+ _statusLabel.Text = "DRY RUN: Plan prepared. No runtime changes and no real drain decision dialog.";
+ Activate();
+ BringToFront();
+ MessageBox.Show(
+ this,
+ ShutdownOperatorPrompts.BuildDryRunNotice(plan, planPath),
+ ShutdownOperatorPrompts.DryRunTitle,
+ MessageBoxButtons.OK,
+ MessageBoxIcon.Information);
+ };
+
+ if (InvokeRequired)
+ {
+ Invoke(showNotice);
+ }
+ else
+ {
+ showNotice();
+ }
+ }
+
///
/// Pauses a real shutdown after the receive-location phase until the operator
/// confirms that the enterprise environment has drained.
@@ -742,25 +781,13 @@ namespace BizTalkPlatformManagementTool.Ui
var confirmed = false;
Action showConfirmation = () =>
{
- var receiveResults = progress.Steps
- .Where(x => string.Equals(x.Kind, OperationStepKind.ReceiveLocation.ToString(), StringComparison.OrdinalIgnoreCase))
- .ToList();
- var succeeded = receiveResults.Count(x => x.Outcome == OperationStepOutcomes.Succeeded);
- var alreadyDisabled = receiveResults.Count(x => x.Outcome == OperationStepOutcomes.AlreadySatisfied);
- var failed = receiveResults.Count(x => x.Outcome == OperationStepOutcomes.Failed);
- var failureWarning = failed == 0
- ? string.Empty
- : "\nWARNING: " + failed + " receive location(s) failed. Review the red Operation Log entries before continuing.\n";
+ _statusLabel.Text = ShutdownOperatorPrompts.WaitingForInboundDrainStatus;
+ Activate();
+ BringToFront();
var result = MessageBox.Show(
- "The receive-location shutdown phase has finished.\n\n"
- + "Disabled successfully: " + succeeded + "\n"
- + "Already disabled: " + alreadyDisabled + "\n"
- + "Failed: " + failed + "\n"
- + failureWarning + "\n"
- + "Keep this dialog open while you verify in BizTalk Group Hub and your enterprise monitoring that no new inbound work arrives and all in-flight service instances/messages have drained.\n\n"
- + "Continue with orchestrations, send ports and host instances?\n\n"
- + "Yes = continue shutdown. No = stop safely and persist all remaining steps as NotExecuted.",
- "Confirm BizTalk Inbound Drain",
+ this,
+ ShutdownOperatorPrompts.BuildInboundDrainConfirmation(progress),
+ ShutdownOperatorPrompts.InboundDrainTitle,
MessageBoxButtons.YesNo,
MessageBoxIcon.Warning,
MessageBoxDefaultButton.Button2);
@@ -792,8 +819,12 @@ namespace BizTalkPlatformManagementTool.Ui
var confirmed = false;
Action showConfirmation = () =>
{
+ _statusLabel.Text = "ACTION REQUIRED: Review the emergency restore plan and answer the owned confirmation dialog.";
+ Activate();
+ BringToFront();
var executableSteps = plan.Steps.Count(x => x.Execute);
var result = MessageBox.Show(
+ this,
"EMERGENCY RESTORE will reconcile " + executableSteps + " step(s) on server '" + server + "'.\n\n"
+ "Source snapshot (will not be overwritten):\n" + sourcePath + "\n\n"
+ "Enterprise Single Sign-On will be ensured Running first. Already-correct states are skipped; isolated failures are recorded and later steps continue.\n\n"
@@ -983,10 +1014,43 @@ namespace BizTalkPlatformManagementTool.Ui
_clearButton.Enabled = !busy;
_openLogsButton.Enabled = !busy;
_closeButton.Enabled = !busy;
- _statusLabel.Text = status;
+ _serverTextBox.Enabled = !busy;
+ _outputTextBox.Enabled = !busy;
+ _stateFileTextBox.Enabled = !busy;
+ _timeoutInput.Enabled = !busy;
+ _pollInput.Enabled = !busy;
+ _dryRunCheckBox.Enabled = !busy;
+ _statusLabel.Text = !busy && string.Equals(status, "Ready.", StringComparison.Ordinal)
+ ? "Ready. " + (_dryRunCheckBox.Checked
+ ? "DRY RUN is enabled; shutdown will not change runtime state or request a drain decision."
+ : "REAL EXECUTION is enabled; shutdown will pause for the owned drain decision.")
+ : status;
});
}
+ /// Updates the prominent execution-mode warning when Dry run changes.
+ /// The checkbox that raised the event.
+ /// The event arguments.
+ private void DryRunCheckBoxCheckedChanged(object sender, EventArgs e)
+ {
+ UpdateDryRunPresentation();
+ if (!_isBusy)
+ {
+ _statusLabel.Text = _dryRunCheckBox.Checked
+ ? "Ready. DRY RUN is enabled; shutdown will not change runtime state or request a drain decision."
+ : "Ready. REAL EXECUTION is enabled; shutdown will pause for the owned drain decision.";
+ }
+ }
+
+ /// Uses explicit text and color instead of relying on a small check mark.
+ private void UpdateDryRunPresentation()
+ {
+ _dryRunCheckBox.Text = _dryRunCheckBox.Checked
+ ? "DRY RUN - no changes / no drain dialog"
+ : "REAL EXECUTION - drain dialog required";
+ _dryRunCheckBox.BackColor = _dryRunCheckBox.Checked ? Color.LightGoldenrodYellow : Color.MistyRose;
+ }
+
///
/// Handles the output directory Browse button click.
///
diff --git a/src/BizTalkPlatformManagementTool/Ui/ShutdownOperatorPrompts.cs b/src/BizTalkPlatformManagementTool/Ui/ShutdownOperatorPrompts.cs
new file mode 100644
index 0000000..0386a64
--- /dev/null
+++ b/src/BizTalkPlatformManagementTool/Ui/ShutdownOperatorPrompts.cs
@@ -0,0 +1,109 @@
+using System;
+using System.Linq;
+using BizTalkPlatformManagementTool.Models;
+
+namespace BizTalkPlatformManagementTool.Ui
+{
+ ///
+ /// Builds stable, testable operator guidance for shutdown plan review, dry-run
+ /// simulation and the real inbound-drain checkpoint.
+ ///
+ internal static class ShutdownOperatorPrompts
+ {
+ /// Title of the non-mutating dry-run explanation.
+ public const string DryRunTitle = "Shutdown Dry Run - No Runtime Changes";
+
+ /// Title of the saved-plan confirmation.
+ public const string PreparedPlanTitle = "Confirm Prepared BizTalk Plan";
+
+ /// Title of the real inbound-drain decision.
+ public const string InboundDrainTitle = "ACTION REQUIRED - Confirm BizTalk Inbound Drain";
+
+ /// Status displayed while the real shutdown waits for drain verification.
+ public const string WaitingForInboundDrainStatus = "ACTION REQUIRED: Shutdown paused. Verify inbound drain and answer the owned confirmation dialog.";
+
+ /// Builds the explicit explanation shown for a shutdown dry-run.
+ /// The prepared shutdown plan.
+ /// The durable path of the prepared plan.
+ /// Operator-facing dry-run guidance.
+ public static string BuildDryRunNotice(OperationPlan plan, string planPath)
+ {
+ if (plan == null)
+ {
+ throw new ArgumentNullException("plan");
+ }
+
+ var checkpointCount = CountCheckpoints(plan);
+ return "DRY RUN is enabled. This run only simulates the saved shutdown plan.\n\n"
+ + "No receive location is disabled and no runtime state is changed. Therefore the real inbound-drain decision dialog will not open during this run.\n\n"
+ + (checkpointCount == 0
+ ? "This plan has no later shutdown phase and therefore needs no drain checkpoint.\n\n"
+ : "The plan contains the inbound-drain checkpoint. It will require an explicit Yes/No decision when the same plan is prepared with Dry run disabled.\n\n")
+ + "Prepared plan:\n" + planPath;
+ }
+
+ /// Builds the confirmation for a fully prepared real operation.
+ /// The operation name.
+ /// The prepared operation plan.
+ /// The selected target server.
+ /// The durable path of the prepared plan.
+ /// Operator-facing prepared-plan confirmation.
+ public static string BuildPreparedPlanConfirmation(string actionName, OperationPlan plan, string server, string planPath)
+ {
+ if (plan == null)
+ {
+ throw new ArgumentNullException("plan");
+ }
+
+ var executableSteps = plan.Steps.Count(x => x.Execute && !IsCheckpoint(x));
+ var checkpointCount = CountCheckpoints(plan);
+ return actionName + " will execute " + executableSteps + " step(s) on server '" + server + "'.\n\n"
+ + (checkpointCount == 0
+ ? string.Empty
+ : "The saved plan contains an operator drain checkpoint after all receive locations. The real shutdown will visibly pause there for a second, explicit decision.\n\n")
+ + "The exact plan was saved to:\n" + planPath + "\n\nContinue now?";
+ }
+
+ /// Builds the real inbound-drain decision from completed receive-location results.
+ /// The execution results already reached.
+ /// Operator-facing drain verification guidance.
+ public static string BuildInboundDrainConfirmation(OperationExecutionReport progress)
+ {
+ if (progress == null)
+ {
+ throw new ArgumentNullException("progress");
+ }
+
+ var receiveResults = progress.Steps
+ .Where(x => string.Equals(x.Kind, OperationStepKind.ReceiveLocation.ToString(), StringComparison.OrdinalIgnoreCase))
+ .ToList();
+ var succeeded = receiveResults.Count(x => x.Outcome == OperationStepOutcomes.Succeeded);
+ var alreadyDisabled = receiveResults.Count(x => x.Outcome == OperationStepOutcomes.AlreadySatisfied);
+ var failed = receiveResults.Count(x => x.Outcome == OperationStepOutcomes.Failed);
+ var failureWarning = failed == 0
+ ? string.Empty
+ : "\nWARNING: " + failed + " receive location(s) failed. Review the red Operation Log entries before continuing.\n";
+
+ return "The receive-location shutdown phase has finished. The shutdown is PAUSED and no later phase can run until you decide.\n\n"
+ + "Disabled successfully: " + succeeded + "\n"
+ + "Already disabled: " + alreadyDisabled + "\n"
+ + "Failed: " + failed + "\n"
+ + failureWarning + "\n"
+ + "Keep this dialog open while you verify in BizTalk Group Hub and your enterprise monitoring that no new inbound work arrives and all in-flight service instances/messages have drained.\n\n"
+ + "Continue with orchestrations, send ports and host instances?\n\n"
+ + "Yes = continue shutdown. No or closing this dialog = stop safely and persist all remaining steps as NotExecuted.";
+ }
+
+ /// Counts executable drain checkpoints in a plan.
+ private static int CountCheckpoints(OperationPlan plan)
+ {
+ return plan.Steps.Count(x => x.Execute && IsCheckpoint(x));
+ }
+
+ /// Checks whether a plan row is the stable operator checkpoint kind.
+ private static bool IsCheckpoint(OperationStep step)
+ {
+ return step != null && string.Equals(step.Kind, OperationStepKind.OperatorCheckpoint.ToString(), StringComparison.OrdinalIgnoreCase);
+ }
+ }
+}
diff --git a/src/BizTalkPlatformManagementTool/app.manifest b/src/BizTalkPlatformManagementTool/app.manifest
index 3d1cb63..58cc45b 100644
--- a/src/BizTalkPlatformManagementTool/app.manifest
+++ b/src/BizTalkPlatformManagementTool/app.manifest
@@ -1,6 +1,6 @@
-
+
diff --git a/tests/BizTalkPlatformManagementTool.Tests/Program.cs b/tests/BizTalkPlatformManagementTool.Tests/Program.cs
index 69303cb..dee8c14 100644
--- a/tests/BizTalkPlatformManagementTool.Tests/Program.cs
+++ b/tests/BizTalkPlatformManagementTool.Tests/Program.cs
@@ -7,6 +7,7 @@ using System.Text;
using BizTalkPlatformManagementTool.Models;
using BizTalkPlatformManagementTool.Services;
using BizTalkPlatformManagementTool.Setup;
+using BizTalkPlatformManagementTool.Ui;
namespace BizTalkPlatformManagementTool.Tests
{
@@ -28,10 +29,12 @@ namespace BizTalkPlatformManagementTool.Tests
Run("Legacy213SnapshotIsAcceptedForRecovery", Legacy213SnapshotIsAcceptedForRecovery);
Run("RestorePlanUsesSafeOrder", RestorePlanUsesSafeOrder);
Run("ShutdownPlanUsesGlobalSafeOrder", ShutdownPlanUsesGlobalSafeOrder);
+ Run("ShutdownPlanRequiresCheckpointBeforeLaterWorkWithoutReceiveLocations", ShutdownPlanRequiresCheckpointBeforeLaterWorkWithoutReceiveLocations);
Run("ShutdownContinuesAcrossArtifactCategoriesAfterReceiveLocationFailure", ShutdownContinuesAcrossArtifactCategoriesAfterReceiveLocationFailure);
Run("ShutdownCheckpointDeclineStopsLaterPhases", ShutdownCheckpointDeclineStopsLaterPhases);
Run("ShutdownCheckpointMissingHandlerFailsClosed", ShutdownCheckpointMissingHandlerFailsClosed);
Run("ShutdownCheckpointDryRunDoesNotPrompt", ShutdownCheckpointDryRunDoesNotPrompt);
+ Run("ShutdownOperatorPromptsDistinguishDryRunAndRealDecision", ShutdownOperatorPromptsDistinguishDryRunAndRealDecision);
Run("EmergencyRestorePlanStartsSsoFirst", EmergencyRestorePlanStartsSsoFirst);
Run("HostInstancePlanAcceptsShortAndFqdnServer", HostInstancePlanAcceptsShortAndFqdnServer);
Run("PlanExecutionContinuesAfterSchedulerFailure", PlanExecutionContinuesAfterSchedulerFailure);
@@ -182,6 +185,18 @@ namespace BizTalkPlatformManagementTool.Tests
Assert(lastSendPort < firstHost, "host instances were not globally last");
}
+ /// Prüft den Checkpoint auch dann, wenn keine aktive Receive Location als Planzeile existiert.
+ private static void ShutdownPlanRequiresCheckpointBeforeLaterWorkWithoutReceiveLocations()
+ {
+ var snapshot = Snapshot("APP", "SEND", ArtifactStates.SendPortStarted);
+ var plan = new BizTalkOperationService(null).CreateShutdownPlan(snapshot, snapshot.Server);
+ var checkpoint = plan.Steps.FindIndex(x => x.Kind == OperationStepKind.OperatorCheckpoint.ToString());
+ var sendPort = plan.Steps.FindIndex(x => x.Kind == OperationStepKind.SendPort.ToString());
+
+ Assert(checkpoint >= 0, "later shutdown work did not create an inbound-drain checkpoint");
+ Assert(checkpoint < sendPort, "checkpoint was not placed before later shutdown work");
+ }
+
/// Prüft, dass Scheduler-Erkennung auch nach Planpersistenz möglich bleibt.
private static void ScheduledReceivePlanCarriesAdapterMetadata()
{
@@ -430,6 +445,25 @@ namespace BizTalkPlatformManagementTool.Tests
Assert(!report.OperatorStopped && string.IsNullOrWhiteSpace(report.CheckpointDecision), "dry-run persisted a real checkpoint decision");
}
+ /// Prüft, dass Dry-run-Hinweis und echter Entscheidungsdialog nicht verwechselt werden können.
+ private static void ShutdownOperatorPromptsDistinguishDryRunAndRealDecision()
+ {
+ var snapshot = Snapshot("APP", "SEND", ArtifactStates.SendPortStarted);
+ snapshot.Applications[0].ReceiveLocations.Add(new ReceiveLocationState { Application = "APP", Name = "RL", Enabled = true });
+ var plan = new BizTalkOperationService(null).CreateShutdownPlan(snapshot, snapshot.Server);
+ var dryRun = ShutdownOperatorPrompts.BuildDryRunNotice(plan, @"C:\evidence\shutdown-plan.json");
+ var prepared = ShutdownOperatorPrompts.BuildPreparedPlanConfirmation("Shutdown", plan, snapshot.Server, @"C:\evidence\shutdown-plan.json");
+ var progress = new OperationExecutionReport();
+ progress.Steps.Add(new OperationStepResult { Kind = OperationStepKind.ReceiveLocation.ToString(), Outcome = OperationStepOutcomes.Succeeded });
+ progress.Steps.Add(new OperationStepResult { Kind = OperationStepKind.ReceiveLocation.ToString(), Outcome = OperationStepOutcomes.Failed });
+ var checkpoint = ShutdownOperatorPrompts.BuildInboundDrainConfirmation(progress);
+
+ Assert(dryRun.Contains("No receive location is disabled") && dryRun.Contains("will not open"), "dry-run notice does not explain the missing real dialog");
+ Assert(prepared.Contains("second, explicit decision"), "real-plan confirmation does not announce the later checkpoint dialog");
+ Assert(checkpoint.Contains("shutdown is PAUSED") && checkpoint.Contains("Disabled successfully: 1") && checkpoint.Contains("Failed: 1"), "checkpoint decision does not expose its blocking state and receive results");
+ Assert(checkpoint.Contains("closing this dialog = stop safely"), "checkpoint close behavior is not explicit");
+ }
+
/// Prüft ENTSSO als erste Voraussetzung des Emergency Restore.
private static void EmergencyRestorePlanStartsSsoFirst()
{