Stufe 4c (#764): Kommentare in C#, WebUI-Skripten, CSS und HTML auf Englisch

Rund 2.600 Kommentarblöcke in 310 C#-Dateien übersetzt, Begründungen vollständig
erhalten: blockweise gleiche Anzahl wie vorher, kein Block unter 60 % seiner
Länge, der Code außerhalb der Kommentare per Tokenizer byte-identisch geprüft.
Zitate deutscher Oberflächentexte und Testdaten bleiben deutsch.

WebUI: Kommentare in JS/CSS/HTML übersetzt, JSDoc-@param an die in 4b
umbenannten Parameter angeglichen.

Dabei behoben:
- 4b hatte "tag" (HTML-Tag) als deutsches "Tag" gelesen und zu "dayValue"
  gemacht (makeElement, svg, Tagesübersicht) - zurück auf tag/day.
- Aufgaben "InProgress" bekamen die Plakettenart "laufend", für die es nie eine
  CSS-Klasse gab - jetzt "success" wie laufende Instanzen.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-10-04 12:45:03 +02:00
co-authored by Claude Opus 5.5
parent da4ceac7d5
commit 837a634a22
311 changed files with 7238 additions and 7252 deletions
@@ -1,9 +1,9 @@
namespace ClawdDotNet.Core.Deploymentcenter.Watchdog;
/// <summary>
/// Die Statuswerte, die der Watchdog kennt. <c>stopped</c> und <c>maintenance</c> sind
/// angekündigte Zustände — der Evaluator lässt solche Monitore in Ruhe, statt wenige
/// Minuten nach einem geplanten Herunterfahren einen Fehlalarm zu erzeugen.
/// The status values the watchdog knows. <c>stopped</c> and <c>maintenance</c> are
/// announced states — the evaluator leaves such monitors alone instead of producing a
/// false alarm a few minutes after a planned shutdown.
/// </summary>
public static class WatchdogStatus
{
@@ -15,26 +15,26 @@ public static class WatchdogStatus
}
/// <summary>
/// Eine selbst ermittelte Teilprüfung. Das Deploymentcenter interpretiert den Namen
/// nicht — es liest nur <see cref="Ok"/> und <see cref="Message"/>. Was „gesund"
/// bedeutet, entscheidet damit jede Anwendung selbst.
/// A self-determined partial check. The Deploymentcenter does not interpret the name
/// — it only reads <see cref="Ok"/> and <see cref="Message"/>. What "healthy"
/// means is thus decided by each application itself.
///
/// <para>Schlägt eine Prüfung fehl, stuft der Server einen als <c>ok</c> gemeldeten
/// Heartbeat auf <c>warning</c> herab. Das ist der Unterschied zwischen „ein Faden
/// läuft" und „die Anwendung tut, was sie soll".</para>
/// <para>If a check fails, the server downgrades a heartbeat reported as <c>ok</c>
/// to <c>warning</c>. That is the difference between "a thread
/// is running" and "the application does what it should".</para>
/// </summary>
public sealed record HealthCheck(bool Ok, string? Message = null);
/// <summary>Momentaufnahme des Instanz-Zustands für einen Heartbeat.</summary>
/// <param name="Status">Einer der Werte aus <see cref="WatchdogStatus"/>.</param>
/// <param name="Message">Kurzbegründung, erscheint im Dashboard.</param>
/// <summary>Snapshot of the instance state for a heartbeat.</summary>
/// <param name="Status">One of the values from <see cref="WatchdogStatus"/>.</param>
/// <param name="Message">Short reason, appears in the dashboard.</param>
/// <param name="Metrics">
/// Nur Zahlen: Das Deploymentcenter legt sie mit Zeitstempel ab (14 Tage) und vergleicht
/// den aktuellen Wert mit dem Sieben-Tage-Schnitt desselben Monitors. Nicht-numerische
/// Werte würden dabei stillschweigend verworfen — beschreibende Angaben gehören
/// deshalb in <paramref name="Message"/> oder in die Checks.
/// Numbers only: the Deploymentcenter stores them with a timestamp (14 days) and compares
/// the current value with the seven-day average of the same monitor. Non-numeric
/// values would be silently discarded — descriptive details therefore belong
/// in <paramref name="Message"/> or in the checks.
/// </param>
/// <param name="Checks">Selbst ermittelter Gesundheitszustand je Teilbereich.</param>
/// <param name="Checks">Self-determined health state per area.</param>
public sealed record InstanceHealth(
string Status,
string? Message,
@@ -47,7 +47,7 @@ public sealed record InstanceHealth(
new Dictionary<string, HealthCheck>());
}
/// <summary>Liefert vor jedem Heartbeat den aktuellen Instanz-Zustand.</summary>
/// <summary>Delivers the current instance state before every heartbeat.</summary>
public interface IInstanceHealthProvider
{
Task<InstanceHealth> GetAsync(CancellationToken ct);
@@ -4,22 +4,22 @@ using ClawdDotNet.Core.Config;
namespace ClawdDotNet.Core.Deploymentcenter.Watchdog;
/// <summary>
/// Leitet den Instanz-Zustand für den Heartbeat ab.
/// Derives the instance state for the heartbeat.
///
/// <para>Ein Heartbeat allein beweist nur, dass ein Faden läuft. Deshalb geht der
/// selbst ermittelte Gesundheitszustand als <c>checks</c> mit — der Server stuft einen
/// als <c>ok</c> gemeldeten Beat herab, sobald eine Prüfung fehlschlägt, und nennt in
/// der Antwort die betroffene. Der klassische Fall, den das abfängt: Der Takt meldet
/// brav <c>ok</c>, während der Aufgaben-Scanner seit einer Stunde tot ist.</para>
/// <para>A heartbeat alone only proves that a thread is running. That's why the
/// self-determined health state goes along as <c>checks</c> — the server downgrades a
/// beat reported as <c>ok</c> as soon as a check fails, and names the affected one in
/// the response. The classic case this catches: the timer dutifully reports
/// <c>ok</c> while the task scanner has been dead for an hour.</para>
///
/// <list type="bullet">
/// <item><c>error</c> — kein OpenRouter-Key konfiguriert (Agenten deaktiviert).</item>
/// <item><c>warning</c> — Tagesbudget der Instanz erschöpft oder Scanner steht.</item>
/// <item><c>ok</c> — sonst.</item>
/// <item><c>error</c> — no OpenRouter key configured (agents disabled).</item>
/// <item><c>warning</c> — daily budget of the instance exhausted or scanner stalled.</item>
/// <item><c>ok</c> — otherwise.</item>
/// </list>
///
/// <para>Die Metriken sind bewusst schlank und ausschließlich numerisch: keine
/// sensiblen Nutzdaten, und nur Zahlen landen im Verlauf.</para>
/// <para>The metrics are deliberately lean and exclusively numeric: no
/// sensitive payload data, and only numbers end up in the history.</para>
/// </summary>
public sealed class InstanceHealthProvider : IInstanceHealthProvider
{
@@ -97,8 +97,8 @@ public sealed class InstanceHealthProvider : IInstanceHealthProvider
checks["budget"] = new(budgetProblem is null, budgetProblem);
}
// Der Instanzname steht in der Meldung, nicht in den Metriken: Metriken sind
// Zahlen, alles andere würde der Server beim Verdichten ohnehin verwerfen.
// The instance name is in the message, not in the metrics: metrics are
// numbers, the server would discard everything else when condensing anyway.
if (!_agentsEnabled)
{
return new InstanceHealth(
@@ -121,7 +121,7 @@ public sealed class InstanceHealthProvider : IInstanceHealthProvider
return new InstanceHealth(WatchdogStatus.Ok, $"{_instanceName}: Betrieb normal.", metrics, checks);
}
/// <summary>0 oder kleiner bedeutet: keine Grenze gesetzt. Deckungsgleich mit BudgetGuard.</summary>
/// <summary>0 or less means: no limit set. Consistent with BudgetGuard.</summary>
private static bool Exceeds(decimal limit, decimal used) => limit > 0 && used >= limit;
private static bool Exceeds(long limit, long used) => limit > 0 && used >= limit;
@@ -2,13 +2,13 @@ using System.Text.Json;
namespace ClawdDotNet.Core.Deploymentcenter.Watchdog;
/// <summary>Was der Server zu einem Heartbeat zurückmeldet.</summary>
/// <param name="State">Der daraus abgeleitete Monitor-Zustand (<c>up</c>, <c>warning</c>, …).</param>
/// <param name="FailingChecks">Welche der mitgeschickten Prüfungen fehlgeschlagen sind.</param>
/// <summary>What the server reports back for a heartbeat.</summary>
/// <param name="State">The monitor state derived from it (<c>up</c>, <c>warning</c>, …).</param>
/// <param name="FailingChecks">Which of the checks sent along failed.</param>
public sealed record WatchdogPingResult(string State, IReadOnlyList<string> FailingChecks);
/// <summary>
/// Sendet Heartbeats und Ereignisse an das Watchdog-Modul des Deploymentcenters.
/// Sends heartbeats and events to the Deploymentcenter's watchdog module.
/// </summary>
public interface IWatchdogClient
{
@@ -20,19 +20,19 @@ public interface IWatchdogClient
}
/// <summary>
/// Watchdog-Anbindung: <c>POST /api/watchdog/v1/ping</c> und
/// Watchdog connection: <c>POST /api/watchdog/v1/ping</c> and
/// <c>POST /api/watchdog/v1/event</c>.
///
/// <para><b>Ein Monitor je Instanz.</b> Der Schlüssel des Monitors ist das Paar
/// <c>source</c> + <c>instance</c> (so das Datenbankschema:
/// <c>UNIQUE KEY uq_monitor (source, instance)</c>). Alle ClawdDotNet-Instanzen melden
/// unter derselben <c>source</c> und tragen ihre eigene <c>instance</c> — damit ist jede
/// laufende Instanz ein eigener Monitor mit eigenem Zustand, eigenem Intervall und
/// eigenem Metrik-Verlauf. Stürzt eine von dreien ab, fällt genau deren Monitor.</para>
/// <para><b>One monitor per instance.</b> The key of the monitor is the pair
/// <c>source</c> + <c>instance</c> (per the database schema:
/// <c>UNIQUE KEY uq_monitor (source, instance)</c>). All ClawdDotNet instances report
/// under the same <c>source</c> and carry their own <c>instance</c> — so every
/// running instance is its own monitor with its own state, its own interval and
/// its own metrics history. If one of three crashes, exactly its monitor fails.</para>
///
/// <para>Der Monitor entsteht beim ersten Heartbeat von selbst (<c>INSERT … ON DUPLICATE
/// KEY UPDATE</c>) — eine Registrierung vorab gibt es nicht mehr und ist auch nicht
/// nötig.</para>
/// <para>The monitor comes into being on the first heartbeat by itself (<c>INSERT … ON DUPLICATE
/// KEY UPDATE</c>) — there is no prior registration any more, and none is
/// needed.</para>
/// </summary>
public sealed class WatchdogClient : IWatchdogClient, IDisposable
{
@@ -77,15 +77,15 @@ public sealed class WatchdogClient : IWatchdogClient, IDisposable
["group"] = _group,
["os"] = _os,
// Landet in watchdog_monitors.app_version. Damit steht im Dashboard, welche
// Fassung eine Instanz gerade fährt — bei mehreren Instanzen der
// Unterschied zwischen „läuft" und „läuft noch auf der alten Version".
// Ends up in watchdog_monitors.app_version. That way the dashboard shows which
// version an instance is currently running — with several instances the
// difference between "runs" and "still runs on the old version".
["version"] = _version
};
// Leere Objekte weglassen: Der Server übernimmt health_json nur, wenn etwas
// mitkommt — ein leeres würde den letzten bekannten Zustand nicht ersetzen,
// aber unnötig Platz im Protokoll kosten.
// Leave out empty objects: the server only takes over health_json if something
// comes along — an empty one would not replace the last known state,
// but would needlessly cost space in the log.
if (health.Checks.Count > 0)
{
payload["checks"] = health.Checks.ToDictionary(
@@ -127,9 +127,9 @@ public sealed class WatchdogClient : IWatchdogClient, IDisposable
}
/// <summary>
/// Ein einmaliges Vorkommnis statt einer zyklischen Meldung. Zulässige
/// <paramref name="kind"/>-Werte siehe <see cref="WatchdogEventKind"/> — der Server
/// weist andere ab.
/// A one-time occurrence instead of a cyclic report. Permitted
/// <paramref name="kind"/> values see <see cref="WatchdogEventKind"/> — the server
/// rejects others.
/// </summary>
public async Task SendEventAsync(
string kind, string severity, string? message, object? meta, CancellationToken ct)
@@ -155,9 +155,9 @@ public sealed class WatchdogClient : IWatchdogClient, IDisposable
}
/// <summary>
/// Die vom Server akzeptierten Ereignisarten. Die frühere Anbindung schickte
/// <c>start</c> und <c>stop</c> — beide stehen nicht auf dieser Liste und wurden
/// stillschweigend als <c>started</c> abgelegt.
/// The event kinds accepted by the server. The former connection sent
/// <c>start</c> and <c>stop</c> — neither is on this list, and they were
/// silently stored as <c>started</c>.
/// </summary>
public static class WatchdogEventKind
{
@@ -3,15 +3,15 @@ using Microsoft.Extensions.Logging;
namespace ClawdDotNet.Core.Deploymentcenter.Watchdog;
/// <summary>
/// Sendet im festen Takt Heartbeats an das Watchdog-Modul und meldet Start und Ende.
/// Ein nicht erreichbares Deploymentcenter darf ClawdDotNet nie beeinträchtigen —
/// alle Sendefehler werden geloggt und verschluckt.
/// Sends heartbeats to the watchdog module at a fixed interval and reports start and end.
/// An unreachable Deploymentcenter must never impair ClawdDotNet —
/// all send errors are logged and swallowed.
///
/// <para><b>Sauberes Beenden.</b> Beim Herunterfahren geht ein Heartbeat mit
/// <c>status: "stopped"</c> raus. Der Evaluator lässt einen so gemeldeten Monitor in
/// Ruhe; ohne das erzeugte jedes geplante Beenden wenige Minuten später einen
/// Fehlalarm. Das reine Ereignis genügt dafür nicht — der Evaluator sieht nur den
/// Monitor-Zustand.</para>
/// <para><b>Clean shutdown.</b> On shutdown a heartbeat with
/// <c>status: "stopped"</c> goes out. The evaluator leaves a monitor reported this way
/// alone; without it every planned shutdown would produce a false alarm a few minutes
/// later. The plain event is not enough for that — the evaluator only sees the
/// monitor state.</para>
/// </summary>
public sealed class WatchdogHeartbeatService : IAsyncDisposable
{
@@ -44,8 +44,8 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
}
/// <summary>
/// Baut Client und Dienst in einem Zug. Wirft nur bei grob falscher Konfiguration
/// (fehlende oder nicht-HTTPS-URL).
/// Builds client and service in one go. Throws only on grossly wrong configuration
/// (missing or non-HTTPS URL).
/// </summary>
public static WatchdogHeartbeatService Create(
string baseUrl, string token, string source, string instance, string group, string os,
@@ -58,18 +58,18 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
public bool IsRunning => _loop is { IsCompleted: false };
/// <summary>Der zuletzt vom Server gemeldete Monitor-Zustand — für die Anzeige.</summary>
/// <summary>The monitor state last reported by the server — for display.</summary>
public string LastState => _lastState;
/// <summary>
/// Meldet das nächste Herunterfahren als <b>Wartung</b> statt als planmäßiges Ende.
/// Reports the next shutdown as <b>maintenance</b> instead of a scheduled end.
///
/// <para>Gedacht für das Einspielen eines Updates: Die Instanz ist gleich weg,
/// kommt aber wieder. <c>stopped</c> wäre die falsche Auskunft — es heißt „bewusst
/// beendet" und lässt den Monitor liegen, bis jemand ihn wieder anfasst.
/// <c>maintenance</c> sagt dasselbe über den Alarm aus, trägt aber die Absicht
/// mit: Im Dashboard ist zu sehen, dass hier gerade aktualisiert wird, statt dass
/// eine Instanz ohne Grund verschwindet.</para>
/// <para>Meant for applying an update: the instance is about to go away,
/// but will come back. <c>stopped</c> would be the wrong information — it means "deliberately
/// ended" and leaves the monitor alone until someone touches it again.
/// <c>maintenance</c> says the same about the alarm, but carries the intent
/// along: the dashboard shows that an update is in progress here, instead of
/// an instance disappearing for no reason.</para>
/// </summary>
public void AnnounceMaintenance(string message)
{
@@ -95,8 +95,8 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
WatchdogEventKind.Started, "info", "Instanz gestartet.", null, ct),
"Start-Ereignis").ConfigureAwait(false);
// Erster Beat sofort, damit ein neuer Monitor nicht erst nach einem vollen
// Intervall im Dashboard auftaucht.
// First beat immediately, so a new monitor does not appear in the dashboard only after a full
// interval.
await BeatAsync(ct).ConfigureAwait(false);
try
@@ -107,7 +107,7 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
}
catch (OperationCanceledException)
{
// Regulärer Stopp.
// Regular stop.
}
}
@@ -124,8 +124,8 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
}
catch (Exception ex)
{
// Selbst wenn die Zustandsermittlung scheitert, soll ein Lebenszeichen
// rausgehen — sonst sieht ein Fehler in unserem Code aus wie ein Ausfall.
// Even if determining the state fails, a sign of life should
// go out — otherwise an error in our code looks like an outage.
_logger.LogWarning(ex, "Watchdog: Zustandsermittlung fehlgeschlagen – melde warning.");
health = new InstanceHealth(
WatchdogStatus.Warning, "Zustand konnte nicht ermittelt werden.",
@@ -162,15 +162,15 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
}
catch (DeploymentcenterException ex) when (ex.IsAuthorizationProblem)
{
// Ein abgelehntes Token ist kein Rauschen: Ohne Eingriff bleibt der Monitor
// für immer stumm, und niemand merkt es, weil ja nichts abstürzt.
// A rejected token is not noise: without intervention the monitor stays
// silent forever, and nobody notices because nothing crashes.
_logger.LogWarning(
"Watchdog: {What} abgelehnt ({Code}) – Token prüfen (Recht watchdog:ping).",
what, ex.Code);
}
catch (Exception ex)
{
// Ausfall des Monitorings darf den Betrieb nie stören.
// A failure of monitoring must never disturb operation.
_logger.LogDebug(ex, "Watchdog: {What} konnte nicht gesendet werden (ignoriert).", what);
}
}
@@ -185,12 +185,12 @@ public sealed class WatchdogHeartbeatService : IAsyncDisposable
if (_loop is not null)
{
try { await _loop.ConfigureAwait(false); }
catch (OperationCanceledException) { /* erwartet */ }
catch (OperationCanceledException) { /* expected */ }
catch (Exception ex) { _logger.LogDebug(ex, "Watchdog: Heartbeat-Schleife endete mit Fehler."); }
}
// Angekündigtes Ende, mit kurzer Frist. Hier wird alles geschluckt (auch ein
// Zeitüberlauf), damit das Herunterfahren nie hängt oder wirft.
// Announced end, with a short deadline. Everything is swallowed here (even a
// timeout), so that shutdown never hangs or throws.
try
{
using var stopCts = new CancellationTokenSource(TimeSpan.FromSeconds(3));