Testanleitung: eigenes X-Display je Projekt fuer Parallelbetrieb
CI / Build & Tests (Linux) (push) Canceled after 0s
CI / Plattformneutralität & Hygiene (push) Canceled after 0s

Damit mehrere Agenten gleichzeitig Oberflaechen testen koennen, bekommt jedes
Projekt eine feste Displaynummer (Vorgabe Richard):

  :99 PolyTrader, :98 Predictalytics, :97 ClawdDotNet, :96 IBKRTrader,
  :95 und absteigend fuer kuenftige Projekte.

Zwei Prozesse auf demselben Display sehen die Fenster des jeweils anderen -
Klicks landen im falschen Programm, Aufnahmen zeigen fremde Anwendungen.

Beim Durcharbeiten fielen zwei weitere Kollisionspunkte auf, die zum
Parallelbetrieb dazugehoeren und mit aufgenommen wurden:

- Screenshot- und Logdateien in /tmp brauchen ein Projektpraefix. /tmp/v_1.png
  ueberschreiben sich parallel laufende Projekte gegenseitig.
- "xvfb-run -a" darf NICHT mehr verwendet werden: Das -a sucht sich selbst ein
  freies Display und beginnt dabei bei :99 - ausgerechnet PolyTraders Nummer.
  Stattdessen "xvfb-run -n <nummer>".

Die Warnung ist nicht theoretisch: Auf der VM lag ein verwaister X98-Socket,
entstanden durch genau so einen "xvfb-run -a"-Aufruf beim smoke-ui-Test, als
:99 belegt war. Er blockierte Predictalytics' reservierte Nummer, ohne dass ein
Prozess lief. Entfernt, und die Anleitung erklaert jetzt, wie man verwaiste
Sockets erkennt (ls /tmp/.X11-unix/ gegen pgrep -a Xvfb lesen) und aufraeumt.

Weiter ergaenzt:
- pkill nie ohne Displaynummer - "pkill Xvfb" erschlaegt die Sitzungen aller
  anderen Projekte.
- DISPLAY in jedem Skript setzen, sonst landet man auf :0, dem echten Desktop.
- Die Skriptvorlage hat jetzt einen Kopf mit PROJEKT/DISP/FENSTER; das ist
  alles, was sich je Projekt unterscheidet. Praktisch gegengetestet.
- Aufraeumabschnitt und Belegungsliste um Displays und Verzeichnisse erweitert.

Nebenbei aufgefallen: Das PolyTrader-Publish lag unter ~/pt-test statt
~/polytrader-test und verletzte damit die eigene Verzeichniskonvention.
Umbenannt.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-08-27 20:49:28 +02:00
co-authored by Claude Opus 5
parent 62570cfdd8
commit 9b2294cb4c
33 changed files with 979 additions and 244 deletions
+16 -1
View File
@@ -1,4 +1,4 @@
using System;
using System;
namespace PolyTraderSharp.Services
{
@@ -69,6 +69,21 @@ namespace PolyTraderSharp.Services
$"„{TimeZoneInfo.Local.Id}\" verwendet. Empfohlen ist die IANA-Schreibweise, z.B. „{DefaultId}\".");
}
/// <summary>
/// Jetzt – in der Anzeige-Zeitzone, mit dem dazu passenden Offset.
///
/// <para>Bewusst <see cref="DateTimeOffset"/> und nicht <see cref="DateTime"/>: Ein
/// umgerechneter <c>DateTime</c> trägt seine Zone nicht mit sich, und beim Formatieren mit
/// <c>zzz</c> setzt .NET dann den Offset des <i>Systems</i> ein. Genau daran lag der
/// Widerspruch, den der Linux-Lauf gezeigt hat: Auf einem UTC-Server meldete die Anwendung
/// „Anzeige-Zeitzone: Europe/Berlin" und stempelte in derselben Zeile mit UTC (§8 des
/// Befundberichts vom 26.08.2026).</para>
/// </summary>
public static DateTimeOffset Now => TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, Current);
/// <summary>Der heutige Tag in der Anzeige-Zeitzone – für Logdatei-Tagesgrenzen.</summary>
public static DateTime Today => Now.Date;
/// <summary>Rechnet einen Zeitstempel in die Anzeige-Zeitzone um (UTC-Kennzeichnung wird beachtet).</summary>
public static DateTime ToDisplay(DateTime value) =>
value.Kind == DateTimeKind.Utc
@@ -1,4 +1,4 @@
using System;
using System;
using System.Diagnostics;
using System.IO;
using System.Threading;
@@ -130,7 +130,13 @@ namespace PolyTraderSharp.Services
{
if (!File.Exists(_settings.MullvadCliPath))
{
_logger.Error($"Mullvad CLI not found at: {_settings.MullvadCliPath}");
// Nennt gleich den plattformueblichen Pfad mit: Ein aus der Windows-Zeit
// uebernommener Wert in server_settings.xml ist auf dem Linux-Server der
// haeufigste Grund, und ohne diesen Hinweis sieht man ihm das nicht an
// (Befund B-5 des Linux-Sichttests).
_logger.Error($"Mullvad CLI nicht gefunden: \"{_settings.MullvadCliPath}\". " +
$"Auf diesem System wird ueblicherweise \"{ServerSettings.DefaultMullvadCliPath}\" erwartet " +
$"(Einstellung \"Mullvad CLI Path\").");
return string.Empty;
}
+20 -5
View File
@@ -1,4 +1,4 @@
using System;
using System;
using System.IO;
using System.Threading.Tasks;
using System.Threading.Channels;
@@ -11,7 +11,17 @@ namespace PolyTraderSharp.Services
{
public string Message { get; }
public LogLevel Level { get; }
public DateTime Timestamp { get; }
/// <summary>
/// Zeitpunkt in der Anzeige-Zeitzone der Anwendung, mit passendem Offset.
///
/// <para>Bis zum 27.08.2026 stand hier <c>DateTime.Now</c> – die Zeit des <i>Systems</i>.
/// Auf einem UTC-Server (dem Normalfall) stempelten die Logs damit UTC, während die
/// Oberfläche Ortszeit anzeigte: Wer einen Vorfall von „22:30" suchte, fand ihn unter
/// 20:30 – zwischen Mitternacht und 2 Uhr sogar in der Logdatei des Vortags
/// (§8 des Befundberichts vom 26.08.2026).</para>
/// </summary>
public DateTimeOffset Timestamp { get; }
/// <summary>Korrelations-ID (z. B. SignalId) für die Log-Forensik; leer wenn ohne Kontext.</summary>
public string CorrelationId { get; }
@@ -20,7 +30,7 @@ namespace PolyTraderSharp.Services
{
Message = message;
Level = level;
Timestamp = DateTime.Now;
Timestamp = AppTimeZone.Now;
CorrelationId = correlationId ?? "";
}
}
@@ -105,7 +115,12 @@ namespace PolyTraderSharp.Services
{
try
{
string dateStr = e.Timestamp.ToString("dd-MM-yyyy");
// ISO-Datum wie bei der JSONL-Datei. Bis zum 26.08.2026 stand hier
// "dd-MM-yyyy" - im selben Logs-Ordner lagen damit zwei Namenskonventionen
// nebeneinander: eine Logrotate-Regel brauchte zwei Muster, und im
// Dateimanager sortierten die .log-Dateien nach Tag statt nach Datum
// (Befund B-2 des Linux-Laufs).
string dateStr = e.Timestamp.ToString("yyyy-MM-dd");
string fileName = $"{dateStr}-{e.Level}.log";
string fullPath = Path.Combine(_logsDirectory, fileName);
@@ -167,7 +182,7 @@ namespace PolyTraderSharp.Services
{
lock (_lock)
{
var cutoff = DateTime.Now - maxAge;
var cutoff = DateTimeOffset.UtcNow - maxAge;
return _history.Where(x => x.Timestamp >= cutoff).ToList();
}
}