# Entfernte Locale-Einstellungen in Agententests

Ein Agent kann denselben Befehl mit denselben Argumenten und Dateien ausführen und auf einem entfernten Host trotzdem ein anderes Ergebnis erhalten. Der fehlende Eingabewert ist oft die Locale des Prozesses. Sie verändert, wie gängige Werkzeuge Text einordnen, Namen sortieren, Zahlen formatieren, Datumswerte ausgeben, eine Codierung wählen und Fehlermeldungen formulieren.

Behandle die Locale als Testdaten und nicht als Dekoration der Maschine. Fixiere sie, wenn ein Test ein stabiles Protokoll erwartet, variiere sie, wenn der Code menschliche Konventionen unterstützen soll, und protokolliere sie, sobald ein Agent eine Prozess- oder SSH-Grenze überschreitet. Sonst kann ein lokal grüner Test einen Fehler beim entfernten Parsen verbergen, bis der Agent auf die falsche Zeile, den falschen Betrag oder das falsche Datum reagiert.

Das ist kein Argument dafür, überall Englisch zu erzwingen. Eine Maschinenschnittstelle und eine Schnittstelle für Menschen haben verschiedene Aufgaben. Eine stabile Maschinenausgabe braucht ein ausdrücklich definiertes Format und eine festgelegte Umgebung. Eine Ausgabe für Benutzer sollte bewusst lokalisiert werden. Wer beides vermischt, lässt eine übersetzte Diagnose als Zustand parsen oder verwandelt ein Dezimalkomma unbemerkt in den falschen Wert.

## Der entfernte Prozess bestimmt das Ergebnis

Die wirksame Locale gehört dem Prozess, der den Befehl ausführt. Die Locale deines Laptops steuert kein Programm, das über SSH läuft, sofern die Variablen nicht ausdrücklich weitergereicht oder gesetzt werden. Eine interaktive entfernte Shell kann sich außerdem von einer nicht interaktiven Befehlssitzung unterscheiden.

POSIX.1-2024 legt die Rangfolge eindeutig fest. Ein nicht leeres `LC_ALL` überschreibt alle Kategorien. Fehlt es, gilt eine Kategorievariable wie `LC_TIME` oder `LC_COLLATE` für ihre jeweilige Kategorie. `LANG` liefert den Standard für alle weiterhin ungesetzten Kategorien. Deshalb bewirkt `LANG=C` nichts, solange ein geerbtes `LC_ALL=de_DE.UTF-8` vorhanden ist.

OpenSSH fügt eine weitere Grenze hinzu. Laut Client-Handbuch wählt `SendEnv` die lokalen Variablen für die Übertragung aus, doch der Server muss sie annehmen. Standardmäßig sendet der Client keine. Mit `SetEnv` lassen sich konkrete Werte anfordern, die der Server ebenfalls akzeptieren muss. Ein Entwickler kann `SendEnv LANG LC_*` in seiner persönlichen SSH-Konfiguration haben, während der zustandslose SSH-Helfer des Agenten diese Konfiguration nicht nutzt. Auch der umgekehrte Fall ist möglich. Auf keines der Ergebnisse sollte man sich ohne Prüfung verlassen.

Startdateien beim Anmelden machen das Bild komplizierter. Eine Distribution kann `LANG` über PAM oder die Systemkonfiguration setzen. Ein Benutzerprofil kann es für eine interaktive Anmeldung ändern. Ein entfernter Befehl durchläuft oft nicht denselben Startpfad. Ein von ihm gestarteter Container kann eigene Einstellungen einführen, und in einem minimalen Image ist die benannte Locale womöglich gar nicht installiert.

Die Korrektur besteht darin, die vorgesehene Umgebung am endgültigen Ausführungspunkt zu setzen. Verlasse dich nicht auf Weiterleitung, wenn der Befehl eine stabile Locale braucht. Schreibe die Zuweisung direkt zum Werkzeug:

```sh
ssh buildbox 'env LC_ALL=C.UTF-8 TZ=UTC command-to-test --format=plain'
```

Damit wird der Testvertrag sichtbar. Fehlt `C.UTF-8`, tritt der Fehler zudem an einer aussagekräftigen Stelle auf, statt stillschweigend die Wahl des Hosts zu übernehmen. Wenn dieser Locale-Name nicht vorausgesetzt werden kann, prüfe die verfügbaren Locales bei der Bereitstellung und nutze einen dokumentierten Ersatz.

## Protokolliere die Umgebung vor der Auswertung

Ein entfernter Fehlerbericht braucht die wirksamen Kategorien, die Codierung, die Zeitzone, die Identität des Werkzeugs und die ursprünglichen Bytes. Nur `LANG` zu erfassen reicht nicht, weil `LC_ALL` oder eine Kategorievariable es überschreiben kann. Wer lediglich decodierten Text speichert, kann den Beleg für einen Codierungsfehler beseitigen.

Führe vor dem untersuchten Befehl eine kleine Diagnose aus:

```sh
env | LC_ALL=C sort | sed -n '/^LANG=/p;/^LC_/p;/^TZ=/p'
printf 'charmap='; locale charmap
printf 'decimal='; locale -k decimal_point 2>/dev/null || true
printf 'date='; date +'%Y-%m-%dT%H:%M:%S%z'
printf 'tool='; command -v sort
sort --version 2>/dev/null | sed -n '1p'
```

Ein typisches Linux-Ergebnis kann diese Form haben:

```text
LANG=de_DE.UTF-8
LC_NUMERIC=de_DE.UTF-8
TZ=Europe/Berlin
charmap=UTF-8
decimal=decimal_point="," 
date=2026-07-24T143105+0200
tool=/usr/bin/sort
sort (GNU coreutils) 9.5
```

Mache aus diesem Beispiel keinen Erwartungswert. Entscheidend ist die Menge der Felder. Manche `locale`-Implementierungen formatieren die Schlüsselwortausgabe anders, und andere Werkzeuge unterstützen `--version` möglicherweise nicht. Erfasse den Exit-Status und die Standardfehlerausgabe jeder Prüfung, damit eine fehlende Funktion nicht wie ein leerer Wert aussieht.

Bewahre bei Codierungsfehlern die Bytes vor dem Decodieren auf. Ein Testprogramm kann Standardausgabe und Standardfehler in getrennte Dateien schreiben, Prüfsummen bilden und anschließend eine Kopie mit der angegebenen Codierung decodieren. Eine hexadezimale Ansicht um das erste ungültige Byte ist wesentlich hilfreicher als ein von der Protokollschicht eingesetztes Ersatzzeichen.

Protokolliere auch den genauen Transportweg. `ssh host command`, `ssh host sh -lc command`, ein interaktives Terminal und ein durch ein Agentenwerkzeug gestarteter Prozess sind verschiedene Ausführungspfade. Sie können andere Shells, Startdateien, Pseudoterminals und Umgebungsfilter wählen. Wenn der fehlerhafte Weg eine Agentenaktion nutzt, reproduziere diesen Weg, statt nur zu beweisen, dass eine von Hand eingegebene Anmeldung funktioniert.

Dieses Diagnosepaket gehört bei jeder Abweichung zum Testartefakt. Es verwandelt „entferntes Sortieren ist unzuverlässig“ in einen Vergleich konkreter Eingaben.

## Fixiere eine Locale für Protokolle, nicht für Menschen

Verwende eine feste Locale, wenn die Befehlsausgabe in einen Parser, Snapshot, Diff, Cache-Schlüssel, eine Bereitstellungsentscheidung oder ein anderes Programm fließt. Verwende die gewünschte menschliche Locale, wenn sich die Ausgabe an eine Person richtet. Das sind getrennte Schnittstellen, selbst wenn ein Befehl derzeit beide erzeugt.

Der verbreitete Rat, überall `LC_ALL=C` zu setzen, ist beliebt, weil er viele Unix-Werkzeuge berechenbar macht und auf POSIX-Systemen verfügbar ist. Als allgemeine Regel ist er falsch. Je nach System und Laufzeit kann die Locale `C` ein auf ASCII ausgerichtetes Zeichenmodell bedeuten. Ein Programm, das Namen wie `Málaga` liest, kann die Bytes ablehnen oder falsch behandeln, obwohl die Sortierung stabil geworden ist.

`C.UTF-8` verbindet auf vielen aktuellen Unix-Systemen eine einfache Sortierung mit UTF-8 und ist deshalb eine praktische Test-Locale. POSIX schreibt diesen genauen Namen jedoch nicht vor. macOS, Linux-Distributionen, Container und Sprachlaufzeiten stellen nicht denselben Katalog bereit. `locale -a` zeigt das Angebot des Hosts, und bereitgestellte Test-Images sollten angeben, welchen Namen sie garantieren.

Eine weitere Unterscheidung muss klar bleiben: Locale-Stabilität ist keine Formatstabilität. Ein festes `LC_ALL` garantiert nicht, dass zwei Werkzeugversionen dieselben Spalten, Abstände, Warnungen oder JSON-Felder ausgeben. Wenn das Werkzeug JSON, NUL-Trenner, Epochensekunden oder eine ausdrückliche Formatzeichenfolge anbietet, wähle auch diese Schnittstelle. Die Locale-Kontrolle entfernt eine Variable, friert das Programm aber nicht ein.

Ein guter Wrapper löscht mögliche Überschreibungen und ergänzt nur das Nötige:

```sh
run_stable() {
  env -u LANGUAGE -u LC_COLLATE -u LC_CTYPE -u LC_MESSAGES \
      -u LC_MONETARY -u LC_NUMERIC -u LC_TIME \
      LC_ALL=C.UTF-8 TZ=UTC "$@"
}
run_stable sort input.txt
```

Wenn die Zielsysteme ein `env` ohne `-u` umfassen, baue stattdessen eine minimale Umgebung. Setze `PATH` ausdrücklich und behalte nur Variablen, die die Anwendung benötigt. Kopiere nicht die gesamte Elternumgebung, um anschließend nur `LANG` zu ändern. Dabei bleiben Kategorievariablen erhalten.

Tests für die Ausgabe an Benutzer gehen umgekehrt vor. Sie wählen bewusst eine unterstützte Locale und prüfen die relevante Konvention. Ein deutscher Berichtstest darf ein Dezimalkomma und deutsche Monatsnamen erwarten. Der Parser hinter dem Bericht sollte intern weiterhin normalisierte Zahlen und Datumswerte austauschen.

## Datumswerte brauchen Format und Zeitzone

Locale und Zeitzone verursachen unterschiedliche Datumsfehler. `LC_TIME` steuert Namen und übliche Darstellungen. `TZ` legt fest, welche bürgerliche Zeit zu einem Zeitpunkt gehört. Das Fixieren des einen fixiert das andere nicht.

GNU Coreutils warnt davor, dass sich die Ausgabe von `date` nicht immer später wieder parsen lässt. Das Handbuch empfiehlt für erzeugte Daten ein sprachunabhängiges Format, eine gregorianische Darstellung und eine eindeutige Zone wie UTC oder `Z`. Dieser Rat ist verlässlicher als ein Snapshot, der unter englischen Einstellungen zufällig besteht.

Wähle für ein Testprotokoll eine ausdrückliche Darstellung:

```sh
env LC_ALL=C.UTF-8 TZ=UTC date +'%Y-%m-%dT%H:%M:%SZ'
```

Die Ausgabe hat die Form `2026-07-24T12:31:05Z`. Wenn der Test statt der aktuellen Uhrzeit einen festen Zeitpunkt braucht, übergib ihn über eine unterstützte Option oder injiziere eine Uhr in die Anwendung. Locale-Kontrolle kann die Zeit nicht anhalten.

Vermeide `%c`, `%x`, `%X`, `%a` und `%b` in Daten, die ein anderes Programm parst. Diese Direktiven fordern absichtlich regionale Konventionen oder übersetzte Namen an. Auch numerische Direktiven können auf manchen Systemen Kalenderbesonderheiten enthalten. Das GNU-Handbuch dokumentiert Locales, die für bestimmte Direktiven alternative Kalender verwenden. Ein scheinbar numerisches Jahr ist deshalb kein universeller Vertrag, solange Format und Locale es nicht festlegen.

Wochennummern sind eine weitere Falle. Kalenderjahr, ISO-Wochenjahr und lokale Wochenkonventionen beantworten rund um Neujahr verschiedene Fragen. Lege fest, welche davon die Geschäftsregel verwendet, und teste Grenzdaten. Eine feste Locale korrigiert keine falsche Kombination aus `%Y-%V`.

Berichte für Menschen sollten Datumswerte am Rand des Systems formatieren. Speichere oder übertrage den Zeitpunkt in stabiler Form und wende erst für die Anzeige Locale und Zone des Lesers an. Wenn ein Agent Zeitstempel mehrerer Hosts vergleichen muss, fordere Epochenwerte oder Zeichenfolgen im Stil von RFC 3339 mit Offset an. Lass ihn nicht raten, ob `03/04/26` den 3. April oder den 4. März meint.

Eine sinnvolle Datumsmatrix enthält eine Locale mit englischen Monatsnamen, eine mit anderen Namen, UTC, eine Zone mit Zeitumstellung sowie Daten nahe an der Umstellung und am Jahreswechsel. Es geht nicht darum, die ganze Welt aufzuzählen. Der Test soll Code aufdecken, der die Konventionen des Entwicklers verallgemeinert hat.

## Die Sortierung muss zum Verbraucher passen

Text hat keine einzige natürliche Reihenfolge. Byte-Reihenfolge, Unicode-Codepunkte und sprachliche Sortierung erzeugen verschiedene Folgen. Tests scheitern, wenn sie eine erwarten und eine andere aufrufen.

Das Handbuch von GNU `sort` erklärt, dass Vergleiche normalerweise die durch `LC_COLLATE` gewählte Reihenfolge verwenden. Für Skripte, die die traditionelle Ordnung benötigen, empfiehlt es ausdrücklich `LC_ALL=C`. Es warnt auch davor, nur `LC_COLLATE` zu setzen, wenn `LC_ALL` sie überschreiben kann oder die Zeichenkategorien inkompatible Codierungen verwenden.

Betrachte dieses Fixture:

```text
Zebra
apple
zebra
Ångström
ábaco
```

Eine Locale im Stil von `C` sortiert gewöhnlich nach codierten Bytes. ASCII-Großbuchstaben stehen dabei vor Kleinbuchstaben und UTF-8-Folgen außerhalb von ASCII später. Eine sprachliche Locale kann Großschreibung und Akzente auf unterschiedlichen Ebenen vergleichen. Übernimm keine vermutete Reihenfolge in einen plattformübergreifenden Artikel oder Test. Führe die tatsächlich unterstützte Locale aus und prüfe die benötigte semantische Eigenschaft.

Für ein reproduzierbares Manifest oder eine Referenzdatei ist die Byte-Reihenfolge meist richtig. Setze `LC_ALL=C`, wenn alle Pfade auf den portablen Zeichensatz begrenzt sind. Andernfalls verwende eine geprüfte UTF-8-Locale und definiere die Ordnungsfunktion im Programm. Für eine Liste, die spanische, schwedische oder deutsche Leser sehen, ist binäre Ordnung ungeeignet. Nutze eine Bibliothek für sprachliche Sortierung mit festgelegter Datenversion, denn die Sortierdaten des Betriebssystems können sich unabhängig vom Code ändern.

Sortieren und Verbinden müssen dieselben Regeln verwenden. GNU Coreutils verlangt konsistente Locales und Optionen für `sort` und `join`. Eine unter einer Ordnung sortierte Datei kann für `join` unter einer anderen unsortiert erscheinen, sodass Treffer fehlen oder Diagnosen entstehen. Dasselbe gilt für `comm`, Duplikatentfernung, Zusammenführung und jede Pipeline, die gleiche Werte nebeneinander erwartet.

Bevorzuge Assertions, die die Absicht ausdrücken. Wenn die Reihenfolge unwichtig ist, vergleiche Mengen oder Maps statt eines zufälligen Snapshots. Wenn Byte-Ordnung zum Protokoll gehört, berechne und benenne sie im Test. Wenn lokalisierte Sortierung die Funktion ist, füge Fixtures mit Akzenten, Groß- und Kleinschreibung sowie Satzzeichen hinzu, die sie von binärer Sortierung unterscheiden.

Ein Agent kann das Problem verschärfen, indem er versuchsweise `sort` ausführt und die erste Zeile dann als „kleinstes“ oder „nächstes“ Element behandelt. Schreibe die Ordnungsregel in seinen Aktionsvertrag. „Wähle das erste Release nach semantischer Version“ ist etwas anderes als „wähle den ersten Dateinamen unter der entfernten Locale“.

## Dezimalkommas brechen Pipelines unbemerkt

`LC_NUMERIC` definiert Dezimalzeichen und Gruppierung für Locale-abhängige Funktionen und manche Befehlsoptionen. Eine als `1,25` dargestellte Zahl kann für einen Menschen richtig und für einen Parser, der `1.25` erwartet, ungültig sein. Schlimmer noch: Ein großzügiger Parser akzeptiert womöglich nur den Präfix und liefert ohne deutlichen Fehler `1`.

GNU `sort -n` verwendet Tausendertrennzeichen und Dezimalzeichen der Locale, wenn es numerische Präfixe erkennt. Pythons `locale.format_string`, `locale.atof` und verwandte Funktionen folgen ebenfalls `LC_NUMERIC`. Das normale `float()` von Python und viele Datenformate tun das nicht. Text ohne klare Grenze zwischen diesen Familien zu übergeben erzeugt einen Fehler, der nur unter bestimmten Einstellungen sichtbar wird.

Halte Protokollzahlen normalisiert. JSON-Zahlen verwenden einen Punkt, Befehlsoptionen dokumentieren gewöhnlich eine feste Grammatik und Datenbankformate definieren ihre eigene Darstellung. Formatiere Komma und Gruppen erst für die Anzeige, nachdem Berechnung und Serialisierung abgeschlossen sind.

Teste Parser mit Werten, an denen stilles Abschneiden auffällt:

```text
0.5
1.25
1234.75
-0.125
```

Führe denselben Vorgang anschließend unter einer Locale mit Dezimalkomma aus. Wenn das Werkzeug lokalisierte Eingaben absichtlich akzeptiert, liefere entsprechende Komma-Fixtures und lehne mehrdeutige Gruppierung ab. Wenn es eine feste Grammatik verspricht, setze die Locale des Befehls und prüfe, dass eine Kommaeingabe klar scheitert.

„Repariere“ beliebige Ausgabe nicht, indem du jedes Komma durch einen Punkt ersetzt. Ein Komma kann Felder trennen, Tausender gruppieren oder im Text stehen. Nutze strukturierte Ausgabe oder einen Parser, der die deklarierte Locale kennt. Wenn der Erzeuger keine Grammatik veröffentlicht, ist seine Ausgabe für Menschen nicht für Automatisierung geeignet.

Auch Shell-Arithmetik vermittelt falsche Sicherheit. Die Shell kann eine feste Syntax verwenden, während ein aufgerufenes `awk`, `printf`, Tabellenkonverter oder eine Sprachlaufzeit die Locale in einzelnen Operationen anwendet. Teste die gesamte Pipeline unter einer Umgebung, statt jeden Befehl einzeln in der Login-Shell zu prüfen.

Geld verlangt noch strengere Behandlung. Speichere die kleinste Einheit oder einen Dezimaltyp mit ausdrücklicher Währung und lokalisiere nur den angezeigten Wert. Ein Agent, der einen Betrag mit einem Grenzwert vergleicht, sollte die normalisierte Zahl erhalten und keinen Bericht für Menschen auslesen.

## Codierungsfehler beginnen vor dem Decodieren

Die Locale kann einem Prozess sagen, wie er Bytefolgen als Zeichen interpretiert. `LC_CTYPE` beeinflusst die Zeichenklassifizierung und ist oft an einen Zeichensatz gebunden. Das betrifft Werkzeuge, die Text teilen, Zeichenklassen erkennen, Großschreibung ändern, Anzeigebreiten berechnen oder Bytes und Zeichenfolgen umwandeln.

UTF-8 auf beiden Maschinen beweist nicht, dass jeder Prozess UTF-8 verwendet. Ein entfernter Dienst kann in der Locale `C` starten, einem minimalen Container können generierte Locale-Daten fehlen oder eine Sprachlaufzeit kann einen eigenen UTF-8-Modus einschalten. Die Python-Dokumentation ist deutlich: Auf manchen Systemen ist die bevorzugte Codierung nur eine Schätzung, und Python UTF-8 Mode kann die Locale-Codierung bei dieser Abfrage ignorieren.

Decodiere an Grenzen ausdrücklich. Wenn der Befehlsvertrag UTF-8 festlegt, lies Bytes und decodiere sie als UTF-8 mit strikter Fehlerbehandlung. Rufe nicht den Plattformstandard auf und hoffe auf das richtige Ergebnis. Wenn Unix beliebige Dateinamen erlaubt, denke daran, dass sie an der Betriebssystemgrenze Bytefolgen sind. Eine erzwungene Umwandlung in normalen Text kann Informationen verlieren. Verwende die besondere Dateisystemcodierung und eine umkehrbare Fehlerstrategie der Laufzeit, sofern vorhanden.

Fehlerersatz hilft bei der Anzeige, ist für Entscheidungen aber gefährlich. Zwei verschiedene ungültige Bytefolgen können zum gleichen sichtbaren Ersatzzeichen werden. Ein Test sollte mit dem Byte-Offset scheitern, die Originalausgabe bewahren und ein kurzes Hex-Fenster zeigen. Diese Daten verraten, ob der Erzeuger eine alte Codierung ausgegeben, eine Folge abgeschnitten oder Binärdaten durch einen Textkanal geschickt hat.

Zeichenklassen brauchen direkte Fixtures. Unter verschiedenen Locales können `[[:alpha:]]`, Großschreibung und Leerraumerkennung andere Zeichen umfassen. POSIX erklärt, dass `LC_CTYPE` bestimmt, wie Bytefolgen zu Zeichen werden und welche Zeichen zu Klassen gehören. Ein Skript, das Namen mit einem Locale-abhängigen Bereich bereinigt, kann entfernt anderen Text annehmen oder entfernen.

Nutze Unicode-bewussten Anwendungscode für menschlichen Text und ausdrückliche ASCII-Regeln für Protokollbezeichner. Lass die Umgebungs-Locale nicht entscheiden, was als Variablenname, Token oder Protokollfeld gilt. Umgekehrt ist ein auf ASCII beschränkter Filter für Personennamen keine Validierung.

Ein kompakter Codierungstest enthält reines ASCII, vorgefertigten Text mit Akzenten, denselben sichtbaren Text mit kombinierenden Zeichen, ein anderes Schriftsystem und eine absichtlich ungültige Bytefolge, wenn die Schnittstelle rohe Bytes zulässt. Prüfe Bytes an Protokollgrenzen und Zeichen nach der Decodierung. Diese Trennung macht Fehler verständlich.

## Eine kleine Locale-Matrix deckt Annahmen auf

Führe die meisten deterministischen Befehlstests unter einer fixierten Locale aus und anschließend eine kleinere Variantensuite, die Annahmen gezielt bricht. Jede installierte Locale zu testen kostet Zeit und liefert dennoch schwache Abdeckung, weil viele dieselben relevanten Konventionen teilen.

Wähle Varianten nach Verhalten:

1. Verwende `C` für portables Byteverhalten und übersetzte Diagnosen, die nicht geparst werden dürfen.
2. Verwende eine verfügbare UTF-8-Locale mit Dezimalpunkt und nicht trivialer Sortierung.
3. Verwende eine UTF-8-Locale mit Dezimalkomma und anderen Datumsnamen.
4. Ergänze eine Locale oder einen Laufzeitmodus, der Codierungsannahmen aufdeckt, falls das Produkt ihn unterstützt.
5. Kombiniere Datumstests mit UTC und einer Zone mit Zeitumstellung.

Stelle diese Locales im Test-Image bereit. Ein übersprungener Test, weil dem Runner Locale-Daten fehlen, ist kein Erfolg. Gib bei einem Einrichtungsfehler `locale -a` aus und mache den benötigten Katalog zum Teil der Image-Definition.

Halte die Matrix nahe am Prozessstart. Übergib in Python eine Kopie der Umgebung an `subprocess.run`, statt die globale Locale in einem Runner mit Threads zu ändern:

```python
import os
import subprocess

def run_case(locale_name):
    child_env = os.environ.copy()
    child_env.update({"LC_ALL": locale_name, "TZ": "UTC"})
    return subprocess.run(
        ["./agent-command", "inspect", "fixtures/names.txt"],
        env=child_env,
        check=False,
        stdout=subprocess.PIPE,
        stderr=subprocess.PIPE,
    )
```

Das Python-Handbuch sagt, dass `setlocale()` auf den meisten Systemen nicht threadsicher ist und eine Eigenschaft des gesamten Programms ändert. Ein Wechsel zwischen Tests kann parallele Fälle gegenseitig beeinflussen. Eine Umgebung für den Kindprozess isoliert den getesteten Befehl und entspricht eher der entfernten Ausführung.

Assertions sollten Exit-Status, Bytes der Standardausgabe, Bytes der Fehlerausgabe und die geparste Bedeutung trennen. `LC_MESSAGES` kann die Sprache der Diagnose ändern, ohne den Fehler zu verändern. Ein Test auf den englischen Satz „No such file“ prüft einen Übersetzungskatalog und nicht die Fehlerbedingung. Bevorzuge Exit-Codes, strukturierte Fehlerfelder oder stabile Bezeichner.

Wenn eine Variante scheitert, grenze nach Kategorie ein. Lösche zunächst `LC_ALL` und setze dann `LANG` und einzelne Kategorien, um Zeit, Sortierung, Zahlenformat, Meldungen oder Zeichenbehandlung als Ursache zu finden. Kategorievariablen sind hervorragende Diagnosewerkzeuge, auch wenn die Produktion ein einziges `LC_ALL` verwendet.

Führe die kleine Matrix für Änderungen an Parsing, Prozessstart, SSH, Berichten oder Fixtures aus. Ein geplanter Lauf kann mehr Betriebssysteme und Werkzeugversionen abdecken. Speichere die Umgebungsdiagnose bei jedem Fehler, damit eine Wiederholung nicht vom Gedächtnis abhängt.

## Agentenaktionen brauchen einen Ausführungsvertrag

Ein autonomer Agent verstärkt Locale-Mehrdeutigkeit, weil er eine plausible Ausgabe mit einer folgenreichen Aktion verbinden kann. Ändert sich die Sortierung, wählt er vielleicht eine andere Datei. Schneidet ein Parser eine Dezimalzahl ab, vergleicht er den falschen Grenzwert. Verschiebt eine Zone das Datum, handelt er am Datensatz des falschen Tages.

Gib entfernten Befehlswerkzeugen einen ausdrücklichen Vertrag mit vier Teilen: die gesetzte Umgebung, die zurückgegebenen Bytes oder strukturierten Daten, die bewahrten Statusinformationen und die verwendete Shell-Semantik. Ergänze Werkzeugversionen oder Funktionsprüfungen, wenn die Ausgabe je nach Implementierung variiert. Ein Prompt kann keine unbeschriebene Prozessgrenze reparieren.

Definiere Erfolg, bevor der Agent die Ausgabe sieht. Exit-Status null kann bedeuten, dass der Befehl endete, nicht dass er einen Datensatz fand. Manche Werkzeuge melden Teilergebnisse mit einer Warnung, andere schreiben Fortschritt auch bei Erfolg in den Standardfehler. Bewahre alle drei Kanäle und lass einen Parser mit festgelegter Grammatik entscheiden, ob das Ergebnis nutzbar ist. Lass ein Sprachmodell Erfolg nicht aus dem Ton einer lokalisierten Meldung ableiten.

Eine nicht unterstützte Locale muss die Einrichtung scheitern lassen und darf keine Überraschung während der Aktion werden. Startet ein Befehl mit `LC_ALL=fr_FR.UTF-8` auf einem Host ohne diese Locale, können Shell oder Laufzeit warnen und zurückfallen, oder das Programm kann abbrechen. Das Testprogramm sollte die Locale zuerst über `locale -a` oder eine Funktionsprüfung bestätigen, den Namen protokollieren und stoppen, wenn das gewünschte Verhalten nicht geprüft werden kann. Ein bei der Bereitstellung gewählter Ersatz ist kontrolliert. Ein mitten in der Agentenaktion gewählter Ersatz ist versteckter Zustand.

Prüfe auch die Interpreterebenen getrennt. Der lokale Prozess baut ein SSH-Argument, der entfernte Anmeldedienst startet die Benutzer-Shell, und diese parst die Befehlszeichenfolge, bevor das Zielprogramm seine Argumente liest. Umgebungszuweisungen und Quoting können sich auf jeder Ebene ändern. Bevorzuge eine argumentbasierte API für entfernte Ausführung, wenn es sie gibt. Falls nur eine Shell-Zeichenfolge möglich ist, teste die exakte Serialisierung mit Leerzeichen, einfachem Anführungszeichen, Zeilenumbruch und Text außerhalb von ASCII. Eine feste Locale korrigiert kein Quoting, doch ein Quotingfehler kann die Zuweisung auf den falschen Befehl anwenden.

Behandle häufig geparste Befehlsausgabe wie ein kleines versioniertes Protokoll. Speichere ein Fixture der Bytes, dokumentiere erwartete Locale und Werkzeugfamilie und lehne unbekannte Formen ab. Wenn ein Upgrade die Form ändert, aktualisiere Parser und Fixture gemeinsam. Das wirkt weniger spektakulär, als den Agenten eine weitere Anzeige „verstehen“ zu lassen, und ist für Befehle vor Schreibaktionen deutlich sicherer.

Bevorzuge bei SSH einen Befehl, der die Umgebung entfernt setzt, statt auf passende Client-Weiterleitung zu hoffen. Setze Quoting in der richtigen Ebene und teste Leerzeichen, Anführungszeichen und Text außerhalb von ASCII. Füge nicht nur für die gewünschte Locale eine Login-Shell hinzu, denn sie importiert auch Aliase, Startskripte und weiteren Zustand.

Halte die ursprünglichen Ergebnisse für eine Prüfung bereit. Sallyport kann SSH-Aktionen über den enthaltenen Helfer `sp-ssh` ausführen, während die SSH-Schlüssel im verschlüsselten Tresor bleiben, und das Activity-Journal protokolliert einzelne Aufrufe. Das macht die Befehlsausgabe nicht Locale-unabhängig, erhält aber eine nützliche Grenze: Der Agent bekommt Ergebnisse, jedoch nicht die dafür eingesetzten Zugangsdaten.

Wenn eine Aktion externen Zustand ändern kann, validiere den geparsten Wert vor dem Schreiben. Verlange ein ausdrückliches Format vom Lesebefehl, lehne nicht decodierbare Bytes ab und füge die protokollierte Locale zur vorgeschlagenen Aktion hinzu. Eine menschliche Freigabe ist nur sinnvoll, wenn die Karte den tatsächlich vom System geparsten Wert zeigt.

Die erste Reparatur einer bestehenden Suite ist konkret. Suche alle entfernten Befehle, deren Ausgabe geparst oder als Snapshot gespeichert wird. Ergänze die Diagnose bei Fehlern, fixiere Locale und Zeitzone am entfernten Prozess und füge eine Locale mit Dezimalkomma sowie eine mit anderer Sortierung als Gegenproben hinzu. Die überraschenden Fehler sind genau die Annahmen, die deine lokale Shell bisher verborgen hat.
