Optionaler Aufsatz auf #157. Gehört zu #140. Entschieden: D10 (2026-09-27, die Pseudonymisierung ist optional), D24 (2026-09-28) und E1–E3 (2026-09-30, unten).
Stand
Erledigt (2026-09-30). Ausgeliefert im laufenden Kandidaten 8.0.0 als beta.8 (76d67e4, die Umsetzung) und beta.9 (08dde00, Schritt 1 der Instruktion nannte jedes Bündel noch „not pseudonymised“). Geprüft:
lokal docs verify, instructions verify und pytest (1749 passed, 3 skipped), dazu derselbe Lauf hermetisch mit env -i
CI-Lauf 455 auf 76d67e4 grün, einschließlich des neuen --pseudonymise-Laufs im Schritt „The bug-report collector works in the distribution“ der exportierten Instanz: Zuordnung und Prüfliste neben dem Bündel, nicht im zip, der Hostname des Containers in keiner Bündeldatei
Der Abschnitt „Umsetzung“ beschreibt, was gebaut ist; er wurde gegen den Baum bei 0899c67 entworfen und ohne Abweichung in der Sache umgesetzt.
Warum
Ein Bugreport enthält Benutzernamen, Hostnamen, Pfade mit Firmennamen, Mailadressen und Remote-URLs. Diese Daten einfach zu entfernen würde genau die Information zerstören, auf die es bei Installationsfehlern ankommt: Pfadstruktur und -tiefe, Leerzeichen, Nicht-ASCII-Zeichen, Längen. Im analysierten Lauf (#140) spielten ein Pfad mit Leerzeichen und Bindestrich sowie ein Windows-Laufwerkspfad als Remote-URL eine direkte Rolle.
Ohne Pseudonymisierung geht das Bündel über einen sicheren Kanal direkt in die Analyse, nicht über den Tracker (D10). Dieses Paket macht den Tracker als Kanal möglich.
Entscheidung D24 (Betreiber, 2026-09-28)
Frage des Betreibers: Lässt sich das mit LLMs so weit automatisieren, dass niemand eine Zuordnungstabelle pflegen muss? Eine kleine Restunsicherheit ist hinnehmbar, der Mensch muss aber darauf hingewiesen werden.
Stufe 1, mechanisch: Das Skript liest bekannte Identitäten aus dem System und ersetzt sie im ganzen Bündel durch Platzhalter, die konsistent (gleiche Identität → gleicher Platzhalter) und formerhaltend sind (Länge, Leerzeichen, Bindestriche, Nicht-ASCII-Klasse, Trennzeichen, Tiefe). Systemkomponenten bleiben lesbar.
Stufe 2, per LLM: Der Agent liest das bereits pseudonymisierte Bündel und benennt verbleibende Angaben zu Personen, Firmen, Kunden, internen Hosts/Domains und Projekten als Kandidaten. Das Skript wendet sie mit derselben Maschine an. Das LLM ersetzt selbst nichts.
Keine Tabellenpflege: Die Zuordnung entsteht automatisch, liegt nur lokal neben dem Bündel, nie im Archiv, und dient nur dem Auflösen von Rückfragen. Löschbar ist sie nach der letzten Anwendung von Stufe 2 (präzisiert durch E3).
Restunsicherheit ausdrücklich benannt: in Instruktion, MANIFEST.md und Abschlussmeldung, vor dem Hochladen.
Wohin die Daten gehen: Für Stufe 2 liest das Modell des Harness das Bündel – dieselbe Stelle, die die Sitzung ohnehin sieht; die Instruktion sagt das.
Entscheidungen 2026-09-30 (Betreiber, jeweils nach Empfehlung)
E1 Voreinstellung: opt-in. Im Skript nur mit --pseudonymise; ohne Flag bleibt das Verhalten von #157 exakt gleich (auch CI und bestehende Tests). Die Instruktion fragt in Schritt 1 nach dem Kanal und empfiehlt die Pseudonymisierung für jeden Kanal außer der direkten Übergabe an den Maintainer über einen sicheren Kanal. Begründung: D10 macht die Pseudonymisierung optional; ein Default-an hätte #157s Verhalten geändert.
E2 Lesegrundlage für Stufe 2: Prüfliste plus Freitext. Das Skript schreibt nach Stufe 1 eine lokale Prüfliste (…review.txt, s. u.) mit den strukturierten Resten (Pfadkomponenten, Hosts, Mailadressen, URL-Segmente). Der Agent liest Prüfliste, CHRONOLOGY.md und MANIFEST.md vollständig, Trace und Transkripte vollständig nur unterhalb von 100 KB je Datei, sonst nur über die Prüfliste. Der Restunsicherheitshinweis nennt genau diese Lücke (Freitext großer Traces/Transkripte). Begründung: Alles vollständig zu lesen sprengt bei großen Traces das Kontextfenster; nur die Chronologie verfehlt die Pfade außerhalb des Repos, die D24 nennt.
E3 Zuordnung vor Stufe 2 nötig. Stufe 2 verweigert ohne Zuordnung mit Exit 1 und lässt das Bündel unverändert. Löschbar ist die Zuordnung jederzeit nach der letzten Anwendung von Stufe 2. Begründung: Stufe 2 braucht das Salz, sonst ersetzt sie ein Wort wie „Beispiel“ anders, als Stufe 1 es im Pfad schon getan hat. Präzisiert D24 Punkt 3 („darf jederzeit gelöscht werden“).
Umsetzung
Aufruf
python tools/bugreport.py --pseudonymise [übrige Sammeloptionen] – sammelt wie bisher, wendet Stufe 1 an, schreibt Zuordnung und Prüfliste, packt das zip.
python tools/bugreport.py --bundle reports/bugreport-<stamp> --candidates FILE – Stufe 2 auf ein vorhandenes, pseudonymisiertes Bündel. Schließt alle Sammeloptionen aus, ist wiederholbar und packt das zip neu (erst Temp-Datei, dann os.replace).
Exit 0/1 wie bisher, nie 42. Weiterhin nur Standardbibliothek (hmac, hashlib, secrets, getpass, socket), Syntax 3.8.
Lokale Dateien – neben dem Bündelverzeichnis, nie darin
Datei
Inhalt
Wer schreibt
reports/bugreport-<stamp>.pseudonyms.json
Zufallssalz, je Eintrag Original, Platzhalter, Art, Stufe
Skript, Stufe 1 und 2
reports/bugreport-<stamp>.review.txt
Prüfliste für Stufe 2 (E2): verbleibende Pfadkomponenten, Hosts, Mailadressen, URL-Segmente außerhalb des Vokabulars, mit Datei und Häufigkeit
Skript, nach Stufe 1
Kandidatendatei (Pfad frei, die Instruktion nennt reports/candidates.txt)
eine Angabe pro Zeile, # leitet einen Kommentar ein
Agent
Alle drei enthalten Originale. Sie liegen außerhalb des Bündelverzeichnisses und können darum weder ins zip noch beim Weitergeben des Verzeichnisses mitgehen. reports/ ist gitignored.
Reihenfolge im Sammellauf
Sammeln → final_pass (Geheimnisse, Titelschutz) → Stufe 1 über alle Textdateien → MANIFEST.md → zip. Das Manifest läuft selbst durch die Ersetzung, bevor die Pseudonymtabelle angehängt wird; es enthält danach kein Original.
Stufe 1: welche Identitäten
Benutzername (getpass.getuser(), USER/USERNAME), unter Windows USERDOMAIN (oft die Firmendomäne) und COMPUTERNAME, Hostname (socket.gethostname(); kein getfqdn(), das an DNS hängen kann).
Home-Verzeichnis und Repo-Wurzel: jede Pfadkomponente außerhalb des Vokabulars. Pfade werden an \ und / zerlegt, unabhängig von der Plattform.
git config user.name und user.email (ganze Adresse, lokaler Teil, Domain).
Remote-URLs aus git remote -v: Host und Pfadsegmente; ein lokaler Pfad als Remote wie die Repo-Wurzel. Ausgenommen ist der öffentliche Ursprung des Stacks (gitea.nehmer.net/torben/chemenu) – er ist öffentlich und für die Analyse von dist upgrade relevant. Der Sammler importiert nichts aus chemenu, darum steht der Wert als Konstante PUBLIC_ORIGIN im Skript, mit Verweis auf chemenu/version.py.
Nicht ersetzt: Identitäten unter 3 Zeichen und Einträge des Vokabulars. Das Manifest nennt ihre Anzahl, nicht ihren Wert.
Platzhalter: konsistent, formerhaltend
Wortweise. Eine Identität wird an allem getrennt, was weder Buchstabe noch Ziffer ist. Trennzeichen bleiben stehen. Ein Wort aus dem Vokabular bleibt; jedes andere wird durch ein Pseudowort aus HMAC-SHA256(salz, wort.lower()) ersetzt: Groß → Groß, klein → klein, Ziffer → Ziffer, Nicht-ASCII-Buchstabe → Nicht-ASCII-Buchstabe gleicher Schreibung aus einer festen Menge. Länge je Wort, damit je Komponente und je Pfad bleibt gleich.
Das Pseudowort hängt am Wort, nicht an der Identität. „Beispiel“ wird in OneDrive - Beispiel GmbH, in beispiel.de und im Stufe-2-Kandidaten Beispiel GmbH gleich ersetzt. So bleiben beide Stufen ohne Abstimmung konsistent.
Injektiv. Ergibt sich ein Pseudowort doppelt oder gleicht es einem Original- oder Vokabularwort, löst ein Zähler im HMAC-Input die Kollision auf.
Schreibung. Treffer ohne Rücksicht auf Groß-/Kleinschreibung (Windows-Pfade, Hostnamen); jedes Vorkommen behält sein eigenes Schreibmuster. Schlüssel ist lower(), nicht casefold(), weil casefold die Länge ändert (ß → ss).
Geheimes Zufallssalz je Bündel (secrets), nur in der Zuordnung. Eine feste Ableitung ließe sich durch Durchprobieren von Namen zurückrechnen; zwei Bündel sind so auch nicht miteinander verknüpfbar.
Treffer
Nur ganze Identitäten: direkt davor und danach steht kein Buchstabe und keine Ziffer (_ gilt als Grenze). Längste zuerst, eine kombinierte Regex, ein Durchlauf je Datei.
Formen: roh und JSON-escaped mit ensure_ascii=True (Trace, Transkripte); der Platzhalter wird in derselben Form eingesetzt.
Alle Textdateien des Bündels, wie final_pass, einschließlich tree-paths.txt.
Bekannte Grenze: Eine Identität, die auch ein gewöhnliches Wort ist (Benutzer max), trifft dieses Wort überall an Wortgrenzen, etwa max( in einem Traceback. Hingenommen: Datenschutz geht vor, und die Pseudonymtabelle im Manifest macht jede Ersetzung sichtbar.
Vokabular (bleibt lesbar)
Eine Liste im Skript; die Tests nageln die Beispiele fest. Inhalt nach Gruppen: Windows-Systemordner (Windows, System32, Program Files, ProgramData, Users, Public, AppData, Local, LocalLow, Roaming, Temp, Microsoft, WindowsApps, Programs, Documents, Desktop, Downloads, OneDrive, dazu DESKTOP/LAPTOP als Hostnamenpräfix); Unix/macOS (home, usr, local, bin, opt, etc, var, tmp, mnt, src, Library, Applications); Werkzeuge (Python, Git, Scripts, venv, chocolatey); Stack (chemenu, wikitool, tools, reports, kb, raw); Rechtsformen (GmbH, AG, KG, SE, Inc, Ltd, LLC); das letzte Label eines Hostnamens (TLD); die Markerwörter des Sammlers (removed, collected, title, depth, len, space), damit Titelschutz-Formen und <removed> lesbar bleiben.
Stufe 2
Nach Stufe 1 schreibt das Skript die Prüfliste (E2). Sie nimmt keine Wörter auf, die bereits Platzhalter sind (bekannt aus der Zuordnung).
Der Agent liest nach E2, schreibt die Kandidatendatei und ersetzt selbst nichts.
Das Skript wendet die Kandidaten mit derselben Maschine an. Kandidaten im Vokabular, unter 3 Zeichen oder im Bündel nicht gefunden meldet es auf stdout und wendet sie nicht an. Es ergänzt die Zuordnung (Stufe 2), aktualisiert Pseudonymtabelle und Hinweis im Manifest und packt das zip neu.
Ohne Zuordnung: Exit 1, Bündel unverändert (E3).
MANIFEST.md
§ Privacy in drei Fassungen: ohne Pseudonymisierung wie in #157; nach Stufe 1 „stage 1 applied, stage 2 not yet applied“; nach Stufe 2 der Restunsicherheitshinweis: Stufe 2 ist das Urteil eines Modells und kann Namen, Firmen, Hosts und Projekte übersehen, vor allem im Freitext von Trace und Transkripten über 100 KB (E2); Formen (Länge, Zeichenklassen, Trennzeichen, Tiefe) bleiben absichtlich erhalten; vor dem Teilen lesen.
§ Pseudonyms: Tabelle Platzhalter | Art (user name, host, domain, home path, repo path, git identity, remote, stage-2 candidate) | Stufe | Vorkommen. Nie ein Original.
Abschlussausgabe des Skripts: Bündel, Archiv, Zuordnung, Prüfliste, der Stufe-2-Befehl und der passende Hinweis.
Instruktion instructions/bug-report.md
§ What the bundle holds: Absatz zur Pseudonymisierung (beide Stufen, lokale Dateien, Restunsicherheit, das Modell des Harness liest das Bündel – D24 Punkt 5).
Schritt 1: nach dem Kanal fragen, Pseudonymisierung nach E1 empfehlen; die Ansage an den Nutzer nennt Maschinen-, Benutzer- und Pfadnamen „unless it is pseudonymised“.
Schritt 4 (neu): Stufe 2 – was zu lesen ist (E2, Schwelle 100 KB), wohin die Kandidaten gehen, der Anwendungsbefehl, „never replace anything in the bundle yourself“.
Schritt 6 „Report the result“: Restunsicherheit nennen; Zuordnung, Prüfliste und Kandidatendatei bleiben lokal und enthalten Originale; die Zuordnung ist nach der letzten Stufe-2-Anwendung löschbar (E3).
Entscheidungspunkte: Zuordnung fehlt vor Stufe 2 (E3: neu sammeln); der Mensch will eine Angabe nachtragen → Stufe 2 erneut mit einer eigenen Kandidatenzeile.
Doc-Pull-through
Geändert: instructions/bug-report.md (oben), reports/CONTRACT.md (§ Bug-report bundles: die drei lokalen Dateien, nie im Archiv; § Retention: Zuordnung nach E3), tools/README.md (Absatz „A script beside the package“: zweiter Modus), INSTALL.md (Troubleshooting-Eintrag: --pseudonymise), CHANGES.md.
Geprüft, unverändert: setup-instance.md, upgrade-instance.md (bieten den Bericht nur an), instructions/gates.md (Stufe 2 ruft kein wikitool auf, keine neue Session-ID), docs/ (keine Begründung berührt), tools/CONTRACT.md (bugreport.py ist kein wikitool-Befehl).
CI: der Schritt „The bug-report collector works in the distribution“ hat einen zweiten Lauf mit --pseudonymise: Zuordnung und Prüfliste liegen neben dem Bündel, nicht im zip; socket.gethostname() des Containers kommt in keiner Datei des Bündels vor (per python3/zipfile, der Container hat kein unzip). Der Benutzer ist dort root, steht im Vokabular und taugt nicht als Prüfwert.
Identitäten kommen über monkeypatch von USER/USERNAME, HOME, COMPUTERNAME/USERDOMAIN, die git-Konfiguration und das Remote der Fixture. 11 neue Tests, zusammen 33:
AK1 mit Remote C:\Users\Max.Muster\OneDrive - Beispiel GmbH\x.git auf Linux.
Konsistenz: dieselbe Identität in environment.json (JSON-escaped), CHRONOLOGY.md und einer wikitool-Ausgabe ergibt denselben Platzhalter; ein Vorkommen in anderer Schreibung behält sein Schreibmuster.
Platzhalter-Einheit: Müller-Lüdenscheidt 42 behält Längen, Klassen und Trennzeichen; injektiv über eine Wortmenge; Vokabularwörter bleiben.
Wortgrenze: Identität Max lässt Maximum unberührt.
AK2 und Idempotenz: zweite Anwendung derselben Kandidaten ändert nichts.
AK3: Zuordnung weder im Verzeichnis noch im zip; ohne Zuordnung verweigert Stufe 2 (E3) und lässt das Bündel unverändert.
AK4: die drei Manifest-Fassungen; kein Original im Manifest.
Zwei Bündel ergeben verschiedene Platzhalter für dieselbe Identität.
Der öffentliche Ursprung bleibt lesbar; kurze und Vokabular-Identitäten bleiben stehen und werden gezählt.
--bundle/--candidates schließt Sammeloptionen aus.
Ohne --pseudonymise entstehen keine zusätzlichen Dateien; die bestehenden 22 Tests aus #157 bleiben unverändert grün.
Akzeptanzkriterien
Aus dem Remote C:\Users\Max.Muster\OneDrive - Beispiel GmbH\x.git wird ein Pfad mit gleicher Tiefe, gleichen Leerzeichen und Bindestrichen und gleicher Länge je Komponente; Max.Muster kommt weder im Bündelverzeichnis noch im zip vor (Stufe 1, Test).
Ein Firmenname, der als fester Kandidat übergeben wird, ist danach im ganzen Bündel und im neu gepackten zip durch denselben formerhaltenden Platzhalter ersetzt, und zwar durch dasselbe Pseudowort, das Stufe 1 für dieses Wort in einem Pfad verwendet hat. Getestet wird die Anwendung; die LLM-Erkennung selbst ist nicht deterministisch testbar.
Zuordnung, Prüfliste und Kandidatendatei liegen außerhalb des Bündelverzeichnisses und nicht im zip; nach dem Löschen der Zuordnung ist das Bündel unverändert vollständig, und Stufe 2 verweigert mit Exit 1, ohne eine Datei des Bündels zu ändern (Test).
Kein Original steht in MANIFEST.md; nach Stufe 2 enthalten MANIFEST.md, die Instruktion und die Abschlussausgabe den Hinweis auf die Restunsicherheit (Test für Manifest und Ausgabe).
Zwei Bündel derselben Maschine verwenden verschiedene Platzhalter für dieselbe Identität (Test).
Ohne --pseudonymise ist das Verhalten gleich wie in #157: bestehende Tests unverändert grün, CI-Schritt ohne Flag unverändert.
Nur Standardbibliothek, Syntax 3.8 (bestehender Test).
CI: Lauf mit --pseudonymise in der exportierten Instanz grün (Lauf 455, danach 457).
Doc-Pull-through wie oben; docs verify, instructions verify, pytest grün.
Version
--minor, --impact medium (beta.8): neue Fähigkeit, drop-in in beide Richtungen (neue Flags, Voreinstellung unverändert nach E1). Dazu --patch, --impact low (beta.9) für den Satz in Schritt 1. Beide Bumps laufen im offenen Kandidaten 8.0.0, der aus anderen Gründen die Kompatibilitätsgrenze kreuzt.
Optionaler Aufsatz auf #157. Gehört zu #140. Entschieden: D10 (2026-09-27, die Pseudonymisierung ist optional), D24 (2026-09-28) und E1–E3 (2026-09-30, unten).
## Stand
**Erledigt (2026-09-30).** Ausgeliefert im laufenden Kandidaten `8.0.0` als `beta.8` (`76d67e4`, die Umsetzung) und `beta.9` (`08dde00`, Schritt 1 der Instruktion nannte jedes Bündel noch „not pseudonymised“). Geprüft:
- lokal `docs verify`, `instructions verify` und `pytest` (1749 passed, 3 skipped), dazu derselbe Lauf hermetisch mit `env -i`
- CI-Lauf [455](https://gitea.nehmer.net/torben/chemenu/actions/runs/455) auf `76d67e4` grün, einschließlich des neuen `--pseudonymise`-Laufs im Schritt „The bug-report collector works in the distribution“ der exportierten Instanz: Zuordnung und Prüfliste neben dem Bündel, nicht im zip, der Hostname des Containers in keiner Bündeldatei
- CI-Lauf [457](https://gitea.nehmer.net/torben/chemenu/actions/runs/457) auf `08dde00` grün
Der Abschnitt „Umsetzung“ beschreibt, was gebaut ist; er wurde gegen den Baum bei `0899c67` entworfen und ohne Abweichung in der Sache umgesetzt.
## Warum
Ein Bugreport enthält Benutzernamen, Hostnamen, Pfade mit Firmennamen, Mailadressen und Remote-URLs. Diese Daten einfach zu entfernen würde genau die Information zerstören, auf die es bei Installationsfehlern ankommt: Pfadstruktur und -tiefe, Leerzeichen, Nicht-ASCII-Zeichen, Längen. Im analysierten Lauf (#140) spielten ein Pfad mit Leerzeichen und Bindestrich sowie ein Windows-Laufwerkspfad als Remote-URL eine direkte Rolle.
Ohne Pseudonymisierung geht das Bündel über einen sicheren Kanal direkt in die Analyse, nicht über den Tracker (D10). Dieses Paket macht den Tracker als Kanal möglich.
## Entscheidung D24 (Betreiber, 2026-09-28)
Frage des Betreibers: Lässt sich das mit LLMs so weit automatisieren, dass niemand eine Zuordnungstabelle pflegen muss? Eine kleine Restunsicherheit ist hinnehmbar, der Mensch muss aber darauf hingewiesen werden.
1. **Stufe 1, mechanisch:** Das Skript liest bekannte Identitäten aus dem System und ersetzt sie im ganzen Bündel durch Platzhalter, die **konsistent** (gleiche Identität → gleicher Platzhalter) und **formerhaltend** sind (Länge, Leerzeichen, Bindestriche, Nicht-ASCII-Klasse, Trennzeichen, Tiefe). Systemkomponenten bleiben lesbar.
2. **Stufe 2, per LLM:** Der Agent liest das bereits pseudonymisierte Bündel und benennt verbleibende Angaben zu Personen, Firmen, Kunden, internen Hosts/Domains und Projekten als Kandidaten. Das Skript wendet sie mit derselben Maschine an. Das LLM ersetzt selbst nichts.
3. **Keine Tabellenpflege:** Die Zuordnung entsteht automatisch, liegt nur lokal neben dem Bündel, nie im Archiv, und dient nur dem Auflösen von Rückfragen. Löschbar ist sie nach der letzten Anwendung von Stufe 2 (präzisiert durch E3).
4. **Restunsicherheit ausdrücklich benannt:** in Instruktion, `MANIFEST.md` und Abschlussmeldung, vor dem Hochladen.
5. **Wohin die Daten gehen:** Für Stufe 2 liest das Modell des Harness das Bündel – dieselbe Stelle, die die Sitzung ohnehin sieht; die Instruktion sagt das.
## Entscheidungen 2026-09-30 (Betreiber, jeweils nach Empfehlung)
- **E1 Voreinstellung: opt-in.** Im Skript nur mit `--pseudonymise`; ohne Flag bleibt das Verhalten von #157 exakt gleich (auch CI und bestehende Tests). Die Instruktion fragt in Schritt 1 nach dem Kanal und empfiehlt die Pseudonymisierung für jeden Kanal außer der direkten Übergabe an den Maintainer über einen sicheren Kanal. Begründung: D10 macht die Pseudonymisierung optional; ein Default-an hätte #157s Verhalten geändert.
- **E2 Lesegrundlage für Stufe 2: Prüfliste plus Freitext.** Das Skript schreibt nach Stufe 1 eine lokale Prüfliste (`…review.txt`, s. u.) mit den strukturierten Resten (Pfadkomponenten, Hosts, Mailadressen, URL-Segmente). Der Agent liest Prüfliste, `CHRONOLOGY.md` und `MANIFEST.md` vollständig, Trace und Transkripte vollständig nur unterhalb von 100 KB je Datei, sonst nur über die Prüfliste. Der Restunsicherheitshinweis nennt genau diese Lücke (Freitext großer Traces/Transkripte). Begründung: Alles vollständig zu lesen sprengt bei großen Traces das Kontextfenster; nur die Chronologie verfehlt die Pfade außerhalb des Repos, die D24 nennt.
- **E3 Zuordnung vor Stufe 2 nötig.** Stufe 2 verweigert ohne Zuordnung mit Exit 1 und lässt das Bündel unverändert. Löschbar ist die Zuordnung jederzeit nach der letzten Anwendung von Stufe 2. Begründung: Stufe 2 braucht das Salz, sonst ersetzt sie ein Wort wie „Beispiel“ anders, als Stufe 1 es im Pfad schon getan hat. Präzisiert D24 Punkt 3 („darf jederzeit gelöscht werden“).
## Umsetzung
### Aufruf
- `python tools/bugreport.py --pseudonymise [übrige Sammeloptionen]` – sammelt wie bisher, wendet Stufe 1 an, schreibt Zuordnung und Prüfliste, packt das zip.
- `python tools/bugreport.py --bundle reports/bugreport-<stamp> --candidates FILE` – Stufe 2 auf ein vorhandenes, pseudonymisiertes Bündel. Schließt alle Sammeloptionen aus, ist wiederholbar und packt das zip neu (erst Temp-Datei, dann `os.replace`).
- Exit 0/1 wie bisher, nie 42. Weiterhin nur Standardbibliothek (`hmac`, `hashlib`, `secrets`, `getpass`, `socket`), Syntax 3.8.
### Lokale Dateien – neben dem Bündelverzeichnis, nie darin
| Datei | Inhalt | Wer schreibt |
|---|---|---|
| `reports/bugreport-<stamp>.pseudonyms.json` | Zufallssalz, je Eintrag Original, Platzhalter, Art, Stufe | Skript, Stufe 1 und 2 |
| `reports/bugreport-<stamp>.review.txt` | Prüfliste für Stufe 2 (E2): verbleibende Pfadkomponenten, Hosts, Mailadressen, URL-Segmente außerhalb des Vokabulars, mit Datei und Häufigkeit | Skript, nach Stufe 1 |
| Kandidatendatei (Pfad frei, die Instruktion nennt `reports/candidates.txt`) | eine Angabe pro Zeile, `#` leitet einen Kommentar ein | Agent |
Alle drei enthalten Originale. Sie liegen außerhalb des Bündelverzeichnisses und können darum weder ins zip noch beim Weitergeben des Verzeichnisses mitgehen. `reports/` ist gitignored.
### Reihenfolge im Sammellauf
Sammeln → `final_pass` (Geheimnisse, Titelschutz) → Stufe 1 über alle Textdateien → `MANIFEST.md` → zip. Das Manifest läuft selbst durch die Ersetzung, bevor die Pseudonymtabelle angehängt wird; es enthält danach kein Original.
### Stufe 1: welche Identitäten
- Benutzername (`getpass.getuser()`, `USER`/`USERNAME`), unter Windows `USERDOMAIN` (oft die Firmendomäne) und `COMPUTERNAME`, Hostname (`socket.gethostname()`; kein `getfqdn()`, das an DNS hängen kann).
- Home-Verzeichnis und Repo-Wurzel: jede Pfadkomponente außerhalb des Vokabulars. Pfade werden an `\` und `/` zerlegt, unabhängig von der Plattform.
- `git config user.name` und `user.email` (ganze Adresse, lokaler Teil, Domain).
- Remote-URLs aus `git remote -v`: Host und Pfadsegmente; ein lokaler Pfad als Remote wie die Repo-Wurzel. **Ausgenommen** ist der öffentliche Ursprung des Stacks (`gitea.nehmer.net/torben/chemenu`) – er ist öffentlich und für die Analyse von `dist upgrade` relevant. Der Sammler importiert nichts aus `chemenu`, darum steht der Wert als Konstante `PUBLIC_ORIGIN` im Skript, mit Verweis auf `chemenu/version.py`.
- **Nicht ersetzt:** Identitäten unter 3 Zeichen und Einträge des Vokabulars. Das Manifest nennt ihre Anzahl, nicht ihren Wert.
### Platzhalter: konsistent, formerhaltend
- **Wortweise.** Eine Identität wird an allem getrennt, was weder Buchstabe noch Ziffer ist. Trennzeichen bleiben stehen. Ein Wort aus dem Vokabular bleibt; jedes andere wird durch ein Pseudowort aus `HMAC-SHA256(salz, wort.lower())` ersetzt: Groß → Groß, klein → klein, Ziffer → Ziffer, Nicht-ASCII-Buchstabe → Nicht-ASCII-Buchstabe gleicher Schreibung aus einer festen Menge. Länge je Wort, damit je Komponente und je Pfad bleibt gleich.
- **Das Pseudowort hängt am Wort, nicht an der Identität.** „Beispiel“ wird in `OneDrive - Beispiel GmbH`, in `beispiel.de` und im Stufe-2-Kandidaten `Beispiel GmbH` gleich ersetzt. So bleiben beide Stufen ohne Abstimmung konsistent.
- **Injektiv.** Ergibt sich ein Pseudowort doppelt oder gleicht es einem Original- oder Vokabularwort, löst ein Zähler im HMAC-Input die Kollision auf.
- **Schreibung.** Treffer ohne Rücksicht auf Groß-/Kleinschreibung (Windows-Pfade, Hostnamen); jedes Vorkommen behält sein eigenes Schreibmuster. Schlüssel ist `lower()`, nicht `casefold()`, weil `casefold` die Länge ändert (ß → ss).
- **Geheimes Zufallssalz** je Bündel (`secrets`), nur in der Zuordnung. Eine feste Ableitung ließe sich durch Durchprobieren von Namen zurückrechnen; zwei Bündel sind so auch nicht miteinander verknüpfbar.
### Treffer
- Nur ganze Identitäten: direkt davor und danach steht kein Buchstabe und keine Ziffer (`_` gilt als Grenze). Längste zuerst, eine kombinierte Regex, ein Durchlauf je Datei.
- Formen: roh und JSON-escaped mit `ensure_ascii=True` (Trace, Transkripte); der Platzhalter wird in derselben Form eingesetzt.
- Alle Textdateien des Bündels, wie `final_pass`, einschließlich `tree-paths.txt`.
- **Bekannte Grenze:** Eine Identität, die auch ein gewöhnliches Wort ist (Benutzer `max`), trifft dieses Wort überall an Wortgrenzen, etwa `max(` in einem Traceback. Hingenommen: Datenschutz geht vor, und die Pseudonymtabelle im Manifest macht jede Ersetzung sichtbar.
### Vokabular (bleibt lesbar)
Eine Liste im Skript; die Tests nageln die Beispiele fest. Inhalt nach Gruppen: Windows-Systemordner (`Windows`, `System32`, `Program Files`, `ProgramData`, `Users`, `Public`, `AppData`, `Local`, `LocalLow`, `Roaming`, `Temp`, `Microsoft`, `WindowsApps`, `Programs`, `Documents`, `Desktop`, `Downloads`, `OneDrive`, dazu `DESKTOP`/`LAPTOP` als Hostnamenpräfix); Unix/macOS (`home`, `usr`, `local`, `bin`, `opt`, `etc`, `var`, `tmp`, `mnt`, `src`, `Library`, `Applications`); Werkzeuge (`Python`, `Git`, `Scripts`, `venv`, `chocolatey`); Stack (`chemenu`, `wikitool`, `tools`, `reports`, `kb`, `raw`); Rechtsformen (`GmbH`, `AG`, `KG`, `SE`, `Inc`, `Ltd`, `LLC`); das letzte Label eines Hostnamens (TLD); die Markerwörter des Sammlers (`removed`, `collected`, `title`, `depth`, `len`, `space`), damit Titelschutz-Formen und `<removed>` lesbar bleiben.
### Stufe 2
- Nach Stufe 1 schreibt das Skript die Prüfliste (E2). Sie nimmt keine Wörter auf, die bereits Platzhalter sind (bekannt aus der Zuordnung).
- Der Agent liest nach E2, schreibt die Kandidatendatei und ersetzt selbst nichts.
- Das Skript wendet die Kandidaten mit derselben Maschine an. Kandidaten im Vokabular, unter 3 Zeichen oder im Bündel nicht gefunden meldet es auf stdout und wendet sie nicht an. Es ergänzt die Zuordnung (Stufe 2), aktualisiert Pseudonymtabelle und Hinweis im Manifest und packt das zip neu.
- Ohne Zuordnung: Exit 1, Bündel unverändert (E3).
### `MANIFEST.md`
- **§ Privacy** in drei Fassungen: ohne Pseudonymisierung wie in #157; nach Stufe 1 „stage 1 applied, stage 2 not yet applied“; nach Stufe 2 der Restunsicherheitshinweis: Stufe 2 ist das Urteil eines Modells und kann Namen, Firmen, Hosts und Projekte übersehen, vor allem im Freitext von Trace und Transkripten über 100 KB (E2); Formen (Länge, Zeichenklassen, Trennzeichen, Tiefe) bleiben absichtlich erhalten; vor dem Teilen lesen.
- **§ Pseudonyms:** Tabelle Platzhalter | Art (user name, host, domain, home path, repo path, git identity, remote, stage-2 candidate) | Stufe | Vorkommen. Nie ein Original.
- **Abschlussausgabe** des Skripts: Bündel, Archiv, Zuordnung, Prüfliste, der Stufe-2-Befehl und der passende Hinweis.
### Instruktion `instructions/bug-report.md`
- § What the bundle holds: Absatz zur Pseudonymisierung (beide Stufen, lokale Dateien, Restunsicherheit, das Modell des Harness liest das Bündel – D24 Punkt 5).
- Schritt 1: nach dem Kanal fragen, Pseudonymisierung nach E1 empfehlen; die Ansage an den Nutzer nennt Maschinen-, Benutzer- und Pfadnamen „unless it is pseudonymised“.
- Schritt 4 (neu): Stufe 2 – was zu lesen ist (E2, Schwelle 100 KB), wohin die Kandidaten gehen, der Anwendungsbefehl, „never replace anything in the bundle yourself“.
- Schritt 6 „Report the result“: Restunsicherheit nennen; Zuordnung, Prüfliste und Kandidatendatei bleiben lokal und enthalten Originale; die Zuordnung ist nach der letzten Stufe-2-Anwendung löschbar (E3).
- Entscheidungspunkte: Zuordnung fehlt vor Stufe 2 (E3: neu sammeln); der Mensch will eine Angabe nachtragen → Stufe 2 erneut mit einer eigenen Kandidatenzeile.
### Doc-Pull-through
- Geändert: `instructions/bug-report.md` (oben), `reports/CONTRACT.md` (§ Bug-report bundles: die drei lokalen Dateien, nie im Archiv; § Retention: Zuordnung nach E3), `tools/README.md` (Absatz „A script beside the package“: zweiter Modus), `INSTALL.md` (Troubleshooting-Eintrag: `--pseudonymise`), `CHANGES.md`.
- Geprüft, unverändert: `setup-instance.md`, `upgrade-instance.md` (bieten den Bericht nur an), `instructions/gates.md` (Stufe 2 ruft kein `wikitool` auf, keine neue Session-ID), `docs/` (keine Begründung berührt), `tools/CONTRACT.md` (`bugreport.py` ist kein `wikitool`-Befehl).
- **CI:** der Schritt „The bug-report collector works in the distribution“ hat einen zweiten Lauf mit `--pseudonymise`: Zuordnung und Prüfliste liegen neben dem Bündel, nicht im zip; `socket.gethostname()` des Containers kommt in keiner Datei des Bündels vor (per `python3`/`zipfile`, der Container hat kein `unzip`). Der Benutzer ist dort `root`, steht im Vokabular und taugt nicht als Prüfwert.
### Tests (`tools/chemenu/tests/test_bugreport.py`, bestehende Fixture)
Identitäten kommen über `monkeypatch` von `USER`/`USERNAME`, `HOME`, `COMPUTERNAME`/`USERDOMAIN`, die git-Konfiguration und das Remote der Fixture. 11 neue Tests, zusammen 33:
1. AK1 mit Remote `C:\Users\Max.Muster\OneDrive - Beispiel GmbH\x.git` auf Linux.
2. Konsistenz: dieselbe Identität in `environment.json` (JSON-escaped), `CHRONOLOGY.md` und einer `wikitool`-Ausgabe ergibt denselben Platzhalter; ein Vorkommen in anderer Schreibung behält sein Schreibmuster.
3. Platzhalter-Einheit: `Müller-Lüdenscheidt 42` behält Längen, Klassen und Trennzeichen; injektiv über eine Wortmenge; Vokabularwörter bleiben.
4. Wortgrenze: Identität `Max` lässt `Maximum` unberührt.
5. AK2 und Idempotenz: zweite Anwendung derselben Kandidaten ändert nichts.
6. AK3: Zuordnung weder im Verzeichnis noch im zip; ohne Zuordnung verweigert Stufe 2 (E3) und lässt das Bündel unverändert.
7. AK4: die drei Manifest-Fassungen; kein Original im Manifest.
8. Zwei Bündel ergeben verschiedene Platzhalter für dieselbe Identität.
9. Der öffentliche Ursprung bleibt lesbar; kurze und Vokabular-Identitäten bleiben stehen und werden gezählt.
10. `--bundle`/`--candidates` schließt Sammeloptionen aus.
11. Ohne `--pseudonymise` entstehen keine zusätzlichen Dateien; die bestehenden 22 Tests aus #157 bleiben unverändert grün.
## Akzeptanzkriterien
- [x] Aus dem Remote `C:\Users\Max.Muster\OneDrive - Beispiel GmbH\x.git` wird ein Pfad mit gleicher Tiefe, gleichen Leerzeichen und Bindestrichen und gleicher Länge je Komponente; `Max.Muster` kommt weder im Bündelverzeichnis noch im zip vor (Stufe 1, Test).
- [x] Ein Firmenname, der als fester Kandidat übergeben wird, ist danach im ganzen Bündel und im neu gepackten zip durch denselben formerhaltenden Platzhalter ersetzt, und zwar durch dasselbe Pseudowort, das Stufe 1 für dieses Wort in einem Pfad verwendet hat. Getestet wird die Anwendung; die LLM-Erkennung selbst ist nicht deterministisch testbar.
- [x] Zuordnung, Prüfliste und Kandidatendatei liegen außerhalb des Bündelverzeichnisses und nicht im zip; nach dem Löschen der Zuordnung ist das Bündel unverändert vollständig, und Stufe 2 verweigert mit Exit 1, ohne eine Datei des Bündels zu ändern (Test).
- [x] Kein Original steht in `MANIFEST.md`; nach Stufe 2 enthalten `MANIFEST.md`, die Instruktion und die Abschlussausgabe den Hinweis auf die Restunsicherheit (Test für Manifest und Ausgabe).
- [x] Zwei Bündel derselben Maschine verwenden verschiedene Platzhalter für dieselbe Identität (Test).
- [x] Ohne `--pseudonymise` ist das Verhalten gleich wie in #157: bestehende Tests unverändert grün, CI-Schritt ohne Flag unverändert.
- [x] Nur Standardbibliothek, Syntax 3.8 (bestehender Test).
- [x] CI: Lauf mit `--pseudonymise` in der exportierten Instanz grün (Lauf 455, danach 457).
- [x] Doc-Pull-through wie oben; `docs verify`, `instructions verify`, `pytest` grün.
## Version
`--minor`, `--impact medium` (`beta.8`): neue Fähigkeit, drop-in in beide Richtungen (neue Flags, Voreinstellung unverändert nach E1). Dazu `--patch`, `--impact low` (`beta.9`) für den Satz in Schritt 1. Beide Bumps laufen im offenen Kandidaten `8.0.0`, der aus anderen Gründen die Kompatibilitätsgrenze kreuzt.
Entwurf neu gefasst. Der Betreiber hat gefragt, ob sich die Pseudonymisierung mit einem LLM automatisieren lässt, ohne dass jemand eine Zuordnungstabelle pflegt. Eine kleine Restunsicherheit ist ihm recht, wenn der Mensch darauf hingewiesen wird. Der neue Entwurf hat deshalb zwei Stufen:
Stufe 2, LLM: liefert Kandidaten; angewendet werden sie mechanisch.
Zuordnungstabelle: entsteht automatisch, bleibt lokal und darf gelöscht werden.
Hinweis: Die Restunsicherheit wird dem Menschen ausdrücklich genannt.
Labels:kind/decision → kind/build. Der Trigger bleibt #157.
**Changelog:**
- **Entwurf neu gefasst.** Der Betreiber hat gefragt, ob sich die Pseudonymisierung mit einem LLM automatisieren lässt, ohne dass jemand eine Zuordnungstabelle pflegt. Eine kleine Restunsicherheit ist ihm recht, wenn der Mensch darauf hingewiesen wird. Der neue Entwurf hat deshalb zwei Stufen:
- **Stufe 1, mechanisch:** ersetzt bekannte Identitäten.
- **Stufe 2, LLM:** liefert Kandidaten; angewendet werden sie mechanisch.
- **Zuordnungstabelle:** entsteht automatisch, bleibt lokal und darf gelöscht werden.
- **Hinweis:** Die Restunsicherheit wird dem Menschen ausdrücklich genannt.
- **Labels:** `kind/decision` → `kind/build`. Der Trigger bleibt #157.
Trigger erfüllt:#157 ist ausgeliefert (beta.5–beta.7). Darum prio/waiting → prio/planned.
Neu: Der Entwurf ist gegen den Baum (0899c67) ausgearbeitet. Er legt fest: Aufruf (--pseudonymise und --bundle/--candidates), drei lokale Dateien außerhalb des Bündels, welche Identitäten Stufe 1 liest, wortweise HMAC-Platzhalter mit geheimem Salz, Trefferregeln, Vokabular, Ablauf von Stufe 2, drei Manifest-Fassungen, Instruktion, Doc-Pull-through, CI-Schritt und 11 Tests.
Neu: die offenen Fragen F1 (Voreinstellung), F2 (was das Modell in Stufe 2 liest) und F3 (Lebensdauer der Zuordnung, berührt den Wortlaut von D24), jeweils mit Empfehlung. Darum kind/build → kind/decision, bis sie beantwortet sind.
Präzisiert: die Akzeptanzkriterien. Neu sind die Konsistenz über beide Stufen, der Schutz vor Verknüpfung zweier Bündel, das unveränderte Verhalten ohne Flag und der CI-Lauf.
**Changelog:**
- **Trigger erfüllt:** #157 ist ausgeliefert (`beta.5`–`beta.7`). Darum `prio/waiting` → `prio/planned`.
- **Neu:** Der Entwurf ist gegen den Baum (`0899c67`) ausgearbeitet. Er legt fest: Aufruf (`--pseudonymise` und `--bundle`/`--candidates`), drei lokale Dateien außerhalb des Bündels, welche Identitäten Stufe 1 liest, wortweise HMAC-Platzhalter mit geheimem Salz, Trefferregeln, Vokabular, Ablauf von Stufe 2, drei Manifest-Fassungen, Instruktion, Doc-Pull-through, CI-Schritt und 11 Tests.
- **Neu:** die offenen Fragen F1 (Voreinstellung), F2 (was das Modell in Stufe 2 liest) und F3 (Lebensdauer der Zuordnung, berührt den Wortlaut von D24), jeweils mit Empfehlung. Darum `kind/build` → `kind/decision`, bis sie beantwortet sind.
- **Präzisiert:** die Akzeptanzkriterien. Neu sind die Konsistenz über beide Stufen, der Schutz vor Verknüpfung zweier Bündel, das unveränderte Verhalten ohne Flag und der CI-Lauf.
Entschieden (Betreiber, 2026-09-30): F1 → E1 (opt-in), F2 → E2 (Prüfliste plus Freitext, Schwelle 100 KB) und F3 → E3 (Stufe 2 braucht die Zuordnung), jeweils nach Empfehlung. Der Abschnitt „Offene Fragen“ ist durch „Entscheidungen 2026-09-30“ ersetzt.
Präzisiert: D24 Punkt 3. Die Zuordnung ist löschbar, sobald Stufe 2 zum letzten Mal angewendet wurde. Das Akzeptanzkriterium zur Zuordnung nennt jetzt ausdrücklich den Exit 1 ohne Änderung am Bündel.
Labels:kind/decision → kind/build. Das Paket ist bereit zur Umsetzung.
**Changelog:**
- **Entschieden (Betreiber, 2026-09-30):** F1 → E1 (opt-in), F2 → E2 (Prüfliste plus Freitext, Schwelle 100 KB) und F3 → E3 (Stufe 2 braucht die Zuordnung), jeweils nach Empfehlung. Der Abschnitt „Offene Fragen“ ist durch „Entscheidungen 2026-09-30“ ersetzt.
- **Präzisiert:** D24 Punkt 3. Die Zuordnung ist löschbar, sobald Stufe 2 zum letzten Mal angewendet wurde. Das Akzeptanzkriterium zur Zuordnung nennt jetzt ausdrücklich den Exit 1 ohne Änderung am Bündel.
- **Labels:** `kind/decision` → `kind/build`. Das Paket ist bereit zur Umsetzung.
Umgesetzt in 8.0.0-beta.8: Stufe 1 (--pseudonymise) und Stufe 2 (--bundle DIR --candidates FILE) in tools/bugreport.py, 11 neue Tests, instructions/bug-report.md, reports/CONTRACT.md, tools/README.md, INSTALL.md, CI-Schritt, CHANGES.md. docs verify, instructions verify und pytest (1749 passed, auch mit env -i) grün. Offen: CI-Lauf nach dem Push; Abschluss per stack-close.
Umgesetzt in `8.0.0-beta.8`: Stufe 1 (`--pseudonymise`) und Stufe 2 (`--bundle DIR --candidates FILE`) in `tools/bugreport.py`, 11 neue Tests, `instructions/bug-report.md`, `reports/CONTRACT.md`, `tools/README.md`, `INSTALL.md`, CI-Schritt, `CHANGES.md`. `docs verify`, `instructions verify` und `pytest` (1749 passed, auch mit `env -i`) grün. Offen: CI-Lauf nach dem Push; Abschluss per `stack-close`.
Abschluss: Body auf den Endstand gebracht (Stand „erledigt“, „Entwurf“ → „Umsetzung“, CI-Kriterium abgehakt mit den Läufen 455/457, Release-Läufe 456/458 grün). Neu gegenüber dem letzten Stand: beta.9 (08dde00) korrigiert Schritt 1 der Instruktion, der jedes Bündel noch „not pseudonymised“ nannte. Sonst keine weiteren Dokumente veraltet (setup-instance.md, upgrade-instance.md, gates.md, docs/, tools/CONTRACT.md geprüft).
Modelle: Entwurf, Versionsteil und Grenzurteil lagen vor dieser Sitzung (Issue-Body, E1–E3 am 2026-09-30 entschieden); Umsetzung, Tests, Doc-Pull-through und beta.8-Bump liefen auf Sonnet 5.5; der Abschluss mit beta.9 auf Opus 5.5.
Abschluss: Body auf den Endstand gebracht (Stand „erledigt“, „Entwurf“ → „Umsetzung“, CI-Kriterium abgehakt mit den Läufen 455/457, Release-Läufe 456/458 grün). Neu gegenüber dem letzten Stand: `beta.9` (`08dde00`) korrigiert Schritt 1 der Instruktion, der jedes Bündel noch „not pseudonymised“ nannte. Sonst keine weiteren Dokumente veraltet (`setup-instance.md`, `upgrade-instance.md`, `gates.md`, `docs/`, `tools/CONTRACT.md` geprüft).
Modelle: Entwurf, Versionsteil und Grenzurteil lagen vor dieser Sitzung (Issue-Body, E1–E3 am 2026-09-30 entschieden); Umsetzung, Tests, Doc-Pull-through und `beta.8`-Bump liefen auf Sonnet 5.5; der Abschluss mit `beta.9` auf Opus 5.5.
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Optionaler Aufsatz auf #157. Gehört zu #140. Entschieden: D10 (2026-09-27, die Pseudonymisierung ist optional), D24 (2026-09-28) und E1–E3 (2026-09-30, unten).
Stand
Erledigt (2026-09-30). Ausgeliefert im laufenden Kandidaten
8.0.0alsbeta.8(76d67e4, die Umsetzung) undbeta.9(08dde00, Schritt 1 der Instruktion nannte jedes Bündel noch „not pseudonymised“). Geprüft:docs verify,instructions verifyundpytest(1749 passed, 3 skipped), dazu derselbe Lauf hermetisch mitenv -i76d67e4grün, einschließlich des neuen--pseudonymise-Laufs im Schritt „The bug-report collector works in the distribution“ der exportierten Instanz: Zuordnung und Prüfliste neben dem Bündel, nicht im zip, der Hostname des Containers in keiner Bündeldatei08dde00grünDer Abschnitt „Umsetzung“ beschreibt, was gebaut ist; er wurde gegen den Baum bei
0899c67entworfen und ohne Abweichung in der Sache umgesetzt.Warum
Ein Bugreport enthält Benutzernamen, Hostnamen, Pfade mit Firmennamen, Mailadressen und Remote-URLs. Diese Daten einfach zu entfernen würde genau die Information zerstören, auf die es bei Installationsfehlern ankommt: Pfadstruktur und -tiefe, Leerzeichen, Nicht-ASCII-Zeichen, Längen. Im analysierten Lauf (#140) spielten ein Pfad mit Leerzeichen und Bindestrich sowie ein Windows-Laufwerkspfad als Remote-URL eine direkte Rolle.
Ohne Pseudonymisierung geht das Bündel über einen sicheren Kanal direkt in die Analyse, nicht über den Tracker (D10). Dieses Paket macht den Tracker als Kanal möglich.
Entscheidung D24 (Betreiber, 2026-09-28)
Frage des Betreibers: Lässt sich das mit LLMs so weit automatisieren, dass niemand eine Zuordnungstabelle pflegen muss? Eine kleine Restunsicherheit ist hinnehmbar, der Mensch muss aber darauf hingewiesen werden.
MANIFEST.mdund Abschlussmeldung, vor dem Hochladen.Entscheidungen 2026-09-30 (Betreiber, jeweils nach Empfehlung)
--pseudonymise; ohne Flag bleibt das Verhalten von #157 exakt gleich (auch CI und bestehende Tests). Die Instruktion fragt in Schritt 1 nach dem Kanal und empfiehlt die Pseudonymisierung für jeden Kanal außer der direkten Übergabe an den Maintainer über einen sicheren Kanal. Begründung: D10 macht die Pseudonymisierung optional; ein Default-an hätte #157s Verhalten geändert.…review.txt, s. u.) mit den strukturierten Resten (Pfadkomponenten, Hosts, Mailadressen, URL-Segmente). Der Agent liest Prüfliste,CHRONOLOGY.mdundMANIFEST.mdvollständig, Trace und Transkripte vollständig nur unterhalb von 100 KB je Datei, sonst nur über die Prüfliste. Der Restunsicherheitshinweis nennt genau diese Lücke (Freitext großer Traces/Transkripte). Begründung: Alles vollständig zu lesen sprengt bei großen Traces das Kontextfenster; nur die Chronologie verfehlt die Pfade außerhalb des Repos, die D24 nennt.Umsetzung
Aufruf
python tools/bugreport.py --pseudonymise [übrige Sammeloptionen]– sammelt wie bisher, wendet Stufe 1 an, schreibt Zuordnung und Prüfliste, packt das zip.python tools/bugreport.py --bundle reports/bugreport-<stamp> --candidates FILE– Stufe 2 auf ein vorhandenes, pseudonymisiertes Bündel. Schließt alle Sammeloptionen aus, ist wiederholbar und packt das zip neu (erst Temp-Datei, dannos.replace).hmac,hashlib,secrets,getpass,socket), Syntax 3.8.Lokale Dateien – neben dem Bündelverzeichnis, nie darin
reports/bugreport-<stamp>.pseudonyms.jsonreports/bugreport-<stamp>.review.txtreports/candidates.txt)#leitet einen Kommentar einAlle drei enthalten Originale. Sie liegen außerhalb des Bündelverzeichnisses und können darum weder ins zip noch beim Weitergeben des Verzeichnisses mitgehen.
reports/ist gitignored.Reihenfolge im Sammellauf
Sammeln →
final_pass(Geheimnisse, Titelschutz) → Stufe 1 über alle Textdateien →MANIFEST.md→ zip. Das Manifest läuft selbst durch die Ersetzung, bevor die Pseudonymtabelle angehängt wird; es enthält danach kein Original.Stufe 1: welche Identitäten
getpass.getuser(),USER/USERNAME), unter WindowsUSERDOMAIN(oft die Firmendomäne) undCOMPUTERNAME, Hostname (socket.gethostname(); keingetfqdn(), das an DNS hängen kann).\und/zerlegt, unabhängig von der Plattform.git config user.nameunduser.email(ganze Adresse, lokaler Teil, Domain).git remote -v: Host und Pfadsegmente; ein lokaler Pfad als Remote wie die Repo-Wurzel. Ausgenommen ist der öffentliche Ursprung des Stacks (gitea.nehmer.net/torben/chemenu) – er ist öffentlich und für die Analyse vondist upgraderelevant. Der Sammler importiert nichts auschemenu, darum steht der Wert als KonstantePUBLIC_ORIGINim Skript, mit Verweis aufchemenu/version.py.Platzhalter: konsistent, formerhaltend
HMAC-SHA256(salz, wort.lower())ersetzt: Groß → Groß, klein → klein, Ziffer → Ziffer, Nicht-ASCII-Buchstabe → Nicht-ASCII-Buchstabe gleicher Schreibung aus einer festen Menge. Länge je Wort, damit je Komponente und je Pfad bleibt gleich.OneDrive - Beispiel GmbH, inbeispiel.deund im Stufe-2-KandidatenBeispiel GmbHgleich ersetzt. So bleiben beide Stufen ohne Abstimmung konsistent.lower(), nichtcasefold(), weilcasefolddie Länge ändert (ß → ss).secrets), nur in der Zuordnung. Eine feste Ableitung ließe sich durch Durchprobieren von Namen zurückrechnen; zwei Bündel sind so auch nicht miteinander verknüpfbar.Treffer
_gilt als Grenze). Längste zuerst, eine kombinierte Regex, ein Durchlauf je Datei.ensure_ascii=True(Trace, Transkripte); der Platzhalter wird in derselben Form eingesetzt.final_pass, einschließlichtree-paths.txt.max), trifft dieses Wort überall an Wortgrenzen, etwamax(in einem Traceback. Hingenommen: Datenschutz geht vor, und die Pseudonymtabelle im Manifest macht jede Ersetzung sichtbar.Vokabular (bleibt lesbar)
Eine Liste im Skript; die Tests nageln die Beispiele fest. Inhalt nach Gruppen: Windows-Systemordner (
Windows,System32,Program Files,ProgramData,Users,Public,AppData,Local,LocalLow,Roaming,Temp,Microsoft,WindowsApps,Programs,Documents,Desktop,Downloads,OneDrive, dazuDESKTOP/LAPTOPals Hostnamenpräfix); Unix/macOS (home,usr,local,bin,opt,etc,var,tmp,mnt,src,Library,Applications); Werkzeuge (Python,Git,Scripts,venv,chocolatey); Stack (chemenu,wikitool,tools,reports,kb,raw); Rechtsformen (GmbH,AG,KG,SE,Inc,Ltd,LLC); das letzte Label eines Hostnamens (TLD); die Markerwörter des Sammlers (removed,collected,title,depth,len,space), damit Titelschutz-Formen und<removed>lesbar bleiben.Stufe 2
MANIFEST.mdInstruktion
instructions/bug-report.mdDoc-Pull-through
instructions/bug-report.md(oben),reports/CONTRACT.md(§ Bug-report bundles: die drei lokalen Dateien, nie im Archiv; § Retention: Zuordnung nach E3),tools/README.md(Absatz „A script beside the package“: zweiter Modus),INSTALL.md(Troubleshooting-Eintrag:--pseudonymise),CHANGES.md.setup-instance.md,upgrade-instance.md(bieten den Bericht nur an),instructions/gates.md(Stufe 2 ruft keinwikitoolauf, keine neue Session-ID),docs/(keine Begründung berührt),tools/CONTRACT.md(bugreport.pyist keinwikitool-Befehl).--pseudonymise: Zuordnung und Prüfliste liegen neben dem Bündel, nicht im zip;socket.gethostname()des Containers kommt in keiner Datei des Bündels vor (perpython3/zipfile, der Container hat keinunzip). Der Benutzer ist dortroot, steht im Vokabular und taugt nicht als Prüfwert.Tests (
tools/chemenu/tests/test_bugreport.py, bestehende Fixture)Identitäten kommen über
monkeypatchvonUSER/USERNAME,HOME,COMPUTERNAME/USERDOMAIN, die git-Konfiguration und das Remote der Fixture. 11 neue Tests, zusammen 33:C:\Users\Max.Muster\OneDrive - Beispiel GmbH\x.gitauf Linux.environment.json(JSON-escaped),CHRONOLOGY.mdund einerwikitool-Ausgabe ergibt denselben Platzhalter; ein Vorkommen in anderer Schreibung behält sein Schreibmuster.Müller-Lüdenscheidt 42behält Längen, Klassen und Trennzeichen; injektiv über eine Wortmenge; Vokabularwörter bleiben.MaxlässtMaximumunberührt.--bundle/--candidatesschließt Sammeloptionen aus.--pseudonymiseentstehen keine zusätzlichen Dateien; die bestehenden 22 Tests aus #157 bleiben unverändert grün.Akzeptanzkriterien
C:\Users\Max.Muster\OneDrive - Beispiel GmbH\x.gitwird ein Pfad mit gleicher Tiefe, gleichen Leerzeichen und Bindestrichen und gleicher Länge je Komponente;Max.Musterkommt weder im Bündelverzeichnis noch im zip vor (Stufe 1, Test).MANIFEST.md; nach Stufe 2 enthaltenMANIFEST.md, die Instruktion und die Abschlussausgabe den Hinweis auf die Restunsicherheit (Test für Manifest und Ausgabe).--pseudonymiseist das Verhalten gleich wie in #157: bestehende Tests unverändert grün, CI-Schritt ohne Flag unverändert.--pseudonymisein der exportierten Instanz grün (Lauf 455, danach 457).docs verify,instructions verify,pytestgrün.Version
--minor,--impact medium(beta.8): neue Fähigkeit, drop-in in beide Richtungen (neue Flags, Voreinstellung unverändert nach E1). Dazu--patch,--impact low(beta.9) für den Satz in Schritt 1. Beide Bumps laufen im offenen Kandidaten8.0.0, der aus anderen Gründen die Kompatibilitätsgrenze kreuzt.Changelog:
kind/decision→kind/build. Der Trigger bleibt #157.Changelog:
beta.5–beta.7). Darumprio/waiting→prio/planned.0899c67) ausgearbeitet. Er legt fest: Aufruf (--pseudonymiseund--bundle/--candidates), drei lokale Dateien außerhalb des Bündels, welche Identitäten Stufe 1 liest, wortweise HMAC-Platzhalter mit geheimem Salz, Trefferregeln, Vokabular, Ablauf von Stufe 2, drei Manifest-Fassungen, Instruktion, Doc-Pull-through, CI-Schritt und 11 Tests.kind/build→kind/decision, bis sie beantwortet sind.Changelog:
kind/decision→kind/build. Das Paket ist bereit zur Umsetzung.Umgesetzt in
8.0.0-beta.8: Stufe 1 (--pseudonymise) und Stufe 2 (--bundle DIR --candidates FILE) intools/bugreport.py, 11 neue Tests,instructions/bug-report.md,reports/CONTRACT.md,tools/README.md,INSTALL.md, CI-Schritt,CHANGES.md.docs verify,instructions verifyundpytest(1749 passed, auch mitenv -i) grün. Offen: CI-Lauf nach dem Push; Abschluss perstack-close.Abschluss: Body auf den Endstand gebracht (Stand „erledigt“, „Entwurf“ → „Umsetzung“, CI-Kriterium abgehakt mit den Läufen 455/457, Release-Läufe 456/458 grün). Neu gegenüber dem letzten Stand:
beta.9(08dde00) korrigiert Schritt 1 der Instruktion, der jedes Bündel noch „not pseudonymised“ nannte. Sonst keine weiteren Dokumente veraltet (setup-instance.md,upgrade-instance.md,gates.md,docs/,tools/CONTRACT.mdgeprüft).Modelle: Entwurf, Versionsteil und Grenzurteil lagen vor dieser Sitzung (Issue-Body, E1–E3 am 2026-09-30 entschieden); Umsetzung, Tests, Doc-Pull-through und
beta.8-Bump liefen auf Sonnet 5.5; der Abschluss mitbeta.9auf Opus 5.5.