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
@@ -5,19 +5,19 @@ using Shouldly;
namespace ClawdDotNet.Core.Tests.Engine;
/// <summary>
/// Regressionstests für Bug B2: _chatContexts[agentId] ist eine geteilte
/// List&lt;ChatMessage&gt;. Nur der Lookup läuft unter Lock — alle Add-Aufrufe im
/// Schleifenkörper sind ungeschützt.
/// Regression tests for bug B2: _chatContexts[agentId] is a shared
/// List&lt;ChatMessage&gt;. Only the lookup runs under lock — all Add calls in the
/// loop body are unprotected.
///
/// Derselbe Agent kann gleichzeitig von mehreren Seiten angestoßen werden:
/// Telegram-/Mail-ToolJob, WebView-Nachricht des Benutzers, send_message eines
/// anderen Agenten. Ergebnis: korrupte Liste, verschränkte Tool-Sequenzen und
/// dadurch HTTP-400-Fehler.
/// The same agent can be triggered from several sides at the same time:
/// Telegram/mail tool job, the user's WebView message, send_message of
/// another agent. Result: corrupt list, interleaved tool sequences and
/// thus HTTP 400 errors.
/// </summary>
public sealed class AgentEngineConcurrencyTests
{
// ═══════════════════════════════════════════════════════════
// E1 — Zwei parallele Chats auf demselben Agenten
// E1 — two parallel chats on the same agent
// ═══════════════════════════════════════════════════════════
[Theory]
@@ -30,8 +30,8 @@ public sealed class AgentEngineConcurrencyTests
.WithTool(FakeTool.Slow(TimeSpan.FromMilliseconds(15)));
var agent = fixture.AddAgent("agent-a", "TestTool");
// Jeder Lauf: ein Tool-Schritt, dann eine Textantwort — unabhängig davon,
// wie die beiden Konversationen ineinander verschränkt werden.
// Every run: one tool step, then a text answer — regardless of
// how the two conversations get interleaved.
fixture.Client.RespondsContextually();
await Task.WhenAll(
@@ -43,7 +43,7 @@ public sealed class AgentEngineConcurrencyTests
}
// ═══════════════════════════════════════════════════════════
// E2 — Stresstest
// E2 — stress test
// ═══════════════════════════════════════════════════════════
[Fact]
@@ -65,8 +65,8 @@ public sealed class AgentEngineConcurrencyTests
var context = fixture.Engine.GetChatContext(agent.AgentId);
ContextInvariants.AssertValid(context);
// Jede der 30 Anfragen muss genau einmal im Kontext stehen — nichts verloren,
// nichts doppelt durch verlorene Schreibzugriffe auf die Liste.
// Each of the 30 requests must be in the context exactly once — nothing lost,
// nothing doubled through lost writes to the list.
var userMessages = context.Count(m => m.Role == "user");
userMessages.ShouldBe(30);
}
@@ -87,7 +87,7 @@ public sealed class AgentEngineConcurrencyTests
var first = fixture.Engine.ChatAsync(agent, "Erste", "test-instance", default);
var second = fixture.Engine.ChatAsync(agent, "Zweite", "test-instance", default);
// Warten, bis beide tatsächlich angelaufen sind.
// Wait until both have actually started.
await WaitUntilAsync(() => fixture.Engine.IsRunning(agent.AgentId));
await Task.Delay(50);
@@ -99,29 +99,29 @@ public sealed class AgentEngineConcurrencyTests
results.ShouldAllBe(r => r.Status == AgentRunStatus.Cancelled);
// Entscheidend: _runningChats[agentId] hielt bisher nur EINE CancellationTokenSource —
// der zweite Lauf überschrieb den ersten. AbortChat brach dann nur einen ab, der
// andere lief bis ins Run-Timeout. Ohne diese Zeitprüfung wäre der Test grün,
// obwohl der Abbruch gar nicht gegriffen hat.
// Crucial: _runningChats[agentId] used to hold only ONE CancellationTokenSource —
// the second run overwrote the first. AbortChat then only aborted one, the
// other ran until the run timeout. Without this time check the test would be green
// although the abort did not take effect at all.
sw.Elapsed.ShouldBeLessThan(TimeSpan.FromSeconds(5),
"AbortChat muss beide Läufe sofort beenden, nicht erst über das Timeout");
}
// ═══════════════════════════════════════════════════════════
// E4 — Gegenprobe: verschiedene Agenten dürfen parallel laufen
// E4 — counter-check: different agents may run in parallel
// ═══════════════════════════════════════════════════════════
[Fact]
public async Task Verschiedene_Agenten_laufen_wirklich_parallel()
{
// Wichtige Gegenprobe: Der Fix für B2 darf nicht versehentlich alle Agenten
// gegeneinander sperren.
// Important counter-check: the fix for B2 must not accidentally lock all agents
// against each other.
//
// Bis zum 2026-09-24 antwortete die Attrappe hier nur mit Text: Das langsame
// Werkzeug lief nie, die Läufe waren sofort fertig, und geprüft wurde nur, ob drei
// leere Läufe unter 450 ms bleiben. Das bestand auch mit gegenseitig gesperrten
// Agenten - und schlug unter Last fehl, weil es die Maschine mass statt der Engine.
// Jetzt ruft jeder Agent das Werkzeug wirklich auf, und bewiesen wird die Überlappung.
// Until 2026-09-24 the fake here only answered with text: the slow
// tool never ran, the runs were finished immediately, and all that was checked was whether three
// empty runs stay below 450 ms. That passed even with agents locking each other
// - and failed under load, because it measured the machine instead of the engine.
// Now every agent really calls the tool, and the overlap is proven.
var tool = FakeTool.Slow(TimeSpan.FromMilliseconds(500));
var fixture = new EngineFixture().WithTool(tool);