Telemetrie-Default nach Installationsform: Tarball opt-in, Clone opt-out - plus Byte-Deckel je Session und Retention ueber die Anzahl der Session-Verzeichnisse #55

Closed
opened 2026-09-04 18:57:02 +00:00 by torben · 3 comments
Owner

Worum es ging

chemenu.telemetry.writer.enabled() war eine Zeile - Telemetrie immer an, WIKI_TRACE=0 das
einzige Opt-out. Für dieses Entwicklungs-Repo richtig: die Traces sind hier das Messinstrument,
mit dem der Stack sich selbst bewertet (eval score, L2). Für eine ausgelieferte Instanz war es
die falsche Voreinstellung - dort hat niemand Telemetrie bestellt, und niemand liest EVALS.md,
bevor die erste Datei geschrieben ist.

Was nicht das Problem war: es verlässt nichts die Maschine. reports/ ist gitignored, kein
Exporter konfiguriert, herstellerseitige aus (enable_otel, enable_telemetry). Es ging um zwei
andere Dinge:

  1. Prompts und Assistant-Antworten im Klartext auf der Platte des Instanzbetreibers - im
    Dev-Repo eine bewusste Entscheidung, in einer fremden Instanz eine Voreinstellung, die der
    Betreiber nie getroffen hat.
  2. Keinerlei Mengenbegrenzung. Einziger Deckel war WIKI_TRACE_MAX_CONTENT (60 KiB pro
    Attribut
    ). Kein Limit pro Trace, keine Retention, kein Prune.

Was umgesetzt wurde

Erledigt in 5.0.0-beta.9, Commit 82a22ea (plus 42646d8, README-Nachzug). Alle vier
Entscheidungen wurden am 2026-09-10 mit dem Betreiber getroffen; die verworfenen Alternativen
stehen in der Kommentarhistorie.

1 - Erkennung der Installationsform: .wikitool-release.json

Vorhanden → ausgeliefert → Default aus. Fehlt → Dev-Checkout → Default an. Der Stamp ist
maschinengeschrieben (Invariante 1) und war bereits der Marker (dist_cmd.py: "has no
.wikitool-release.json - not a distribution export"). .git schied aus (setup-instance.md
Schritt 2 macht auch die Tarball-Instanz zum Git-Repo), instructions/dev/ hätte den
Telemetrie-Default an die Skill-Schicht gekoppelt.

Zwei beabsichtigte Folgen: die private Instanz fällt auf die opt-in-Seite (ihr Baum stammt aus
einem dist export und trägt den Stamp - eine konsumierende Instanz, kein Messstand), und der
Marker wird gegen config.ROOT aufgelöst, nicht gegen config._PACKAGE_ROOT.

Umsetzungsdetail, das erst beim Bauen auffiel: RELEASE_STAMP_FILENAME ist nach config.py
gewandert und wird von version.py nur noch re-exportiert. chemenu.telemetry muss ohne venv
importierbar bleiben (ein Hook-Handler tut das bei jedem Tool-Call), durfte also nicht
version.py importieren - und eine zweite Kopie des Dateinamens wäre Invariante 8 gewesen. Alle
bestehenden Aufrufer über version_mod.RELEASE_STAMP_FILENAME bleiben unverändert gültig.

2 - Mengenbegrenzung: zwei unabhängige Deckel, beide fail-silent

a) Byte-Deckel pro Session-Trace. Default 5 MiB, WIKI_TRACE_MAX_SESSION_BYTES überschreibt.
Ein stat vor jedem Append. Zum Vergleich: größter Trace in diesem Checkout 36 KB, Baum gesamt
1,9 MB.

b) Verhalten am Limit. Ab Limit wird nichts mehr geschrieben außer einem einmaligen
telemetry.limit-Event (neu in schema.OPTIONAL_EVENTS), ausgelost per
open(<session>/.limit, "x") - dieselbe Ein-Schreiber-Wahl wie bei _seed_session_header.
Rotation wurde verworfen (macht reader.py und eval score mehrdateifähig).

c) Retention über die Anzahl der Session-Verzeichnisse. Default 250,
WIKI_TRACE_KEEP_SESSIONS überschreibt. Läuft nur beim Anlegen eines neuen
Session-Verzeichnisses, sortiert nach mtime wie reader.sessions().

Korrektur gegenüber dem ursprünglichen Entwurf, gefunden beim Testschreiben: die Retention
reserviert den Platz der gerade entstehenden Session (behält keep - 1 des Bestands), statt
auf keep zu trimmen und die neue danach als keep + 1 landen zu lassen. Die naive Variante
konvergiert unter einem stetigen Zustrom einzelner neuer Sessions nie zurück auf keep - jeder
Lauf räumt nur, was der vorige über dem Limit gelassen hat, und der Bestand pendelt dauerhaft
bei keep + 1.

Der Default 250 liegt bewusst über dem Bestand dieses Checkouts zum Zeitpunkt der Umsetzung (229
Verzeichnisse): der Deckel greift in Zukunft, der Altbestand blieb unangetastet.

Löschregel: entfernt werden ausschließlich trace.jsonl und .limit der überzähligen
Verzeichnisse; rmdir nur, wenn danach leer, nie rmtree. Alles andere unter reports/ bleibt
byte-identisch - reports/ hält lokale, nicht rekonstruierbare Daten, die eval score liest.

3 - Opt-in: .wikitool-telemetry.json, im Setup-Interview gefragt

{ "enabled": true, "max_session_bytes": 5242880, "keep_sessions": 250 }

Alle drei Schlüssel optional. Vorrang: Umgebungsvariable > Datei > Formdefault.
Per-Checkout und gitignored wie .wikitool-remotes.json: die Zustimmung, Klartext-Prompts auf
diese Platte zu schreiben, gehört dem Checkout, nicht dem Korpus. Nicht von dist export
ausgeliefert, kein .template; setup-instance.md Schritt 10 schreibt sie aus der Antwort des
Betreibers.

Cache-Detail: policy.resolve() cacht nur die dateisystemseitige Hälfte (Stamp +
Konfigurationsdatei), gekeyt auf den aufgelösten Root. Die Umgebungsvariablen werden bei jedem
Aufruf frisch gelesen - sie kosten keine I/O, und ein Test, der eine davon mitten im Lauf
umsetzt, muss den neuen Wert sofort sehen, nicht einen gecachten. Das war im Entwurf noch als
"alles cachen, gekeyt auf den Root" formuliert und wäre so falsch gewesen.

4 - Versionsteil: --minor auf den laufenden Kandidaten

Drop-in-Test bestanden, vorwärts wie rückwärts: additiv, kein Hand-Work, kein Migrationsskript;
die alte Version ignoriert Datei und Variablen. 5.0.0-beta.85.0.0-beta.9. Der Kandidat
hatte die Grenze längst überschritten (Confidence-Entfernung), der --minor eskaliert ihn nicht
(max-wins) und brauchte weder --breaking noch ein zweites Boundary-Gespräch.

Akzeptanzkriterien

  • In einem Baum ohne .wikitool-release.json, ohne gesetzte Umgebungsvariable, schreibt
    ein wikitool-Aufruf einen Trace.
  • In einem Baum mit .wikitool-release.json, ohne Umgebungsvariable und ohne Opt-in,
    schreibt derselbe Aufruf nichts - und legt reports/telemetry/ gar nicht erst an.
    Zusätzlich zum Unit-Test von Hand gegen einen frischen dist export durchgespielt.
  • WIKI_TRACE überschreibt beide Defaults in beide Richtungen.
  • .wikitool-telemetry.json mit "enabled": true schaltet in der ausgelieferten Instanz
    auf, ohne gesetzte Umgebungsvariable; WIKI_TRACE=0 schlägt die Datei.
  • Nach Erreichen des Byte-Deckels beendet ein weiterer Aufruf seine Arbeit unverändert mit
    Exit 0; die Trace-Datei überschreitet die Grenze um höchstens ein Event, und
    telemetry.limit steht dort genau einmal, auch nach weiteren Aufrufen.
  • Die Durchsetzung löscht ausschließlich trace.jsonl/.limit der überzähligen
    Verzeichnisse. Der Test mit einer fremden Datei im gelöschten Verzeichnis prüft, dass diese
    byte-identisch stehen bleibt und das Verzeichnis nicht entfernt wird.
  • Das Verzeichnis der laufenden Session wird von deren eigenem Retention-Lauf nie entfernt.
  • wikitool doctor meldet den Telemetrie-Zustand: an/aus, warum, Menge gegen beide Deckel -
    und nie FAIL.
  • Der MCP-Server startet in einer ausgelieferten Instanz ohne Opt-in ohne WIKI_TRACE=0 und
    verweigert im Dev-Checkout weiterhin. check_trace_destination liest jetzt
    policy.resolve() statt WIKI_TRACE selbst - es gibt genau eine Stelle, die den Zustand
    auflöst.
  • EVALS.md, INSTALL.md, INSTALL-MCP.md, reports/CONTRACT.md, tools/CONTRACT.md
    beschreiben Default, Opt-in und beide Deckel; docs verify und instructions verify grün.
    Nachgezogen in 42646d8: README.md § "Telemetry and evaluation" behauptete weiter
    "Every wikitool call appends an event" - in einer ausgelieferten Instanz ab jetzt falsch.
    Beim stack-close-Durchgang gefunden, nicht beim ersten Publish.
  • Der neue Default ist im Testlauf festgenagelt, nicht geerbt: jeder Telemetrie-Test setzt
    die Installationsform explizit, die neuen Variablen stehen in _WIKITOOL_ENV, und
    policy.reset_cache() hängt in hermetic_environment.
  • CI-Replay prüft, dass im exportierten Baum kein reports/telemetry/ entsteht
    (.gitea/workflows/ci.yml, § "The distribution works as a fresh instance").

Was verifiziert wurde

  • pytest: 1118 Tests grün (vorher 1095, also 23 neue), zweimal gelaufen - normal und gegen
    eine leere Maschine nach testing-conventions.md Schritt 6, mit identischem Ergebnis.
  • tools/wikitool docs verify und tools/wikitool instructions verify: grün.
  • tools/wikitool doctor: neuer telemetry-Check meldet im Dev-Checkout "on (no
    .wikitool-release.json - dev checkout, default on); 231/250 session(s), 378.242 byte(s)".
  • Hand-Replay eines dist export in einen frischen Baum (Identität, Personalization,
    Conventions, instructions sync, index rebuild, doctor, docs verify,
    instructions verify, lint --fail-on-error): doctor meldet dort "off
    (.wikitool-release.json present - distributed instance, default off)", und
    reports/telemetry/ existiert nach dem gesamten Durchlauf nicht.
  • CI: Runs 228 und 229 auf 82a22ea, Run 230 auf 42646d8, dazu der Nightly-Lauf 231 - alle
    vier success, inklusive des setup-instance.md-Replays gegen einen frischen dist export.

Ein Nebenbefund ohne Handlungsbedarf: pytest --cov lässt 271 unabhängige Tests
(test_xref.py, test_type_resolver.py, test_types_cmd.py, …) fehlschlagen, die ohne --cov
grün sind. Bestand schon vor dieser Änderung und ist von ihr unabhängig - eigenes Issue wert,
falls die Coverage-Zahl je gebraucht wird.

Berührte Stellen

tools/chemenu/telemetry/policy.py (neu), tools/chemenu/telemetry/writer.py,
tools/chemenu/telemetry/schema.py, tools/chemenu/commands/doctor.py,
tools/chemenu/mcp/server.py, tools/chemenu/config.py, tools/chemenu/version.py,
.gitignore, .gitea/workflows/ci.yml, tools/chemenu/tests/conftest.py,
tools/chemenu/tests/test_telemetry_policy.py (neu), test_telemetry_emit.py,
test_doctor.py, test_mcp_server.py, EVALS.md, README.md, INSTALL.md, INSTALL-MCP.md,
reports/CONTRACT.md, tools/CONTRACT.md, instructions/setup-instance.md, CHANGES.md,
VERSION. Nicht berührt: dist_cmd.ROOT_FILES - die Konfigurationsdatei ist per-Checkout und
wird nicht ausgeliefert.

## Worum es ging `chemenu.telemetry.writer.enabled()` war eine Zeile - Telemetrie immer an, `WIKI_TRACE=0` das einzige Opt-out. Für dieses Entwicklungs-Repo richtig: die Traces sind hier das Messinstrument, mit dem der Stack sich selbst bewertet (`eval score`, L2). Für eine ausgelieferte Instanz war es die falsche Voreinstellung - dort hat niemand Telemetrie bestellt, und niemand liest `EVALS.md`, bevor die erste Datei geschrieben ist. **Was nicht das Problem war:** es verlässt nichts die Maschine. `reports/` ist gitignored, kein Exporter konfiguriert, herstellerseitige aus (`enable_otel`, `enable_telemetry`). Es ging um zwei andere Dinge: 1. **Prompts und Assistant-Antworten im Klartext auf der Platte** des Instanzbetreibers - im Dev-Repo eine bewusste Entscheidung, in einer fremden Instanz eine Voreinstellung, die der Betreiber nie getroffen hat. 2. **Keinerlei Mengenbegrenzung.** Einziger Deckel war `WIKI_TRACE_MAX_CONTENT` (60 KiB *pro Attribut*). Kein Limit pro Trace, keine Retention, kein Prune. ## Was umgesetzt wurde Erledigt in `5.0.0-beta.9`, Commit `82a22ea` (plus `42646d8`, README-Nachzug). Alle vier Entscheidungen wurden am 2026-09-10 mit dem Betreiber getroffen; die verworfenen Alternativen stehen in der Kommentarhistorie. ### 1 - Erkennung der Installationsform: `.wikitool-release.json` Vorhanden → ausgeliefert → Default **aus**. Fehlt → Dev-Checkout → Default **an**. Der Stamp ist maschinengeschrieben (Invariante 1) und war bereits *der* Marker (`dist_cmd.py`: "has no `.wikitool-release.json` - not a distribution export"). `.git` schied aus (`setup-instance.md` Schritt 2 macht auch die Tarball-Instanz zum Git-Repo), `instructions/dev/` hätte den Telemetrie-Default an die Skill-Schicht gekoppelt. Zwei beabsichtigte Folgen: die private Instanz fällt auf die opt-in-Seite (ihr Baum stammt aus einem `dist export` und trägt den Stamp - eine konsumierende Instanz, kein Messstand), und der Marker wird gegen `config.ROOT` aufgelöst, nicht gegen `config._PACKAGE_ROOT`. Umsetzungsdetail, das erst beim Bauen auffiel: `RELEASE_STAMP_FILENAME` ist nach `config.py` gewandert und wird von `version.py` nur noch re-exportiert. `chemenu.telemetry` muss ohne venv importierbar bleiben (ein Hook-Handler tut das bei jedem Tool-Call), durfte also nicht `version.py` importieren - und eine zweite Kopie des Dateinamens wäre Invariante 8 gewesen. Alle bestehenden Aufrufer über `version_mod.RELEASE_STAMP_FILENAME` bleiben unverändert gültig. ### 2 - Mengenbegrenzung: zwei unabhängige Deckel, beide fail-silent **a) Byte-Deckel pro Session-Trace.** Default 5 MiB, `WIKI_TRACE_MAX_SESSION_BYTES` überschreibt. Ein `stat` vor jedem Append. Zum Vergleich: größter Trace in diesem Checkout 36 KB, Baum gesamt 1,9 MB. **b) Verhalten am Limit.** Ab Limit wird nichts mehr geschrieben außer einem einmaligen `telemetry.limit`-Event (neu in `schema.OPTIONAL_EVENTS`), ausgelost per `open(<session>/.limit, "x")` - dieselbe Ein-Schreiber-Wahl wie bei `_seed_session_header`. Rotation wurde verworfen (macht `reader.py` und `eval score` mehrdateifähig). **c) Retention über die Anzahl der Session-Verzeichnisse.** Default 250, `WIKI_TRACE_KEEP_SESSIONS` überschreibt. Läuft nur beim Anlegen eines neuen Session-Verzeichnisses, sortiert nach `mtime` wie `reader.sessions()`. Korrektur gegenüber dem ursprünglichen Entwurf, gefunden beim Testschreiben: die Retention **reserviert den Platz der gerade entstehenden Session** (behält `keep - 1` des Bestands), statt auf `keep` zu trimmen und die neue danach als `keep + 1` landen zu lassen. Die naive Variante konvergiert unter einem stetigen Zustrom einzelner neuer Sessions nie zurück auf `keep` - jeder Lauf räumt nur, was der *vorige* über dem Limit gelassen hat, und der Bestand pendelt dauerhaft bei `keep + 1`. Der Default 250 liegt bewusst über dem Bestand dieses Checkouts zum Zeitpunkt der Umsetzung (229 Verzeichnisse): der Deckel greift in Zukunft, der Altbestand blieb unangetastet. **Löschregel:** entfernt werden ausschließlich `trace.jsonl` und `.limit` der überzähligen Verzeichnisse; `rmdir` nur, wenn danach leer, nie `rmtree`. Alles andere unter `reports/` bleibt byte-identisch - `reports/` hält lokale, nicht rekonstruierbare Daten, die `eval score` liest. ### 3 - Opt-in: `.wikitool-telemetry.json`, im Setup-Interview gefragt ```json { "enabled": true, "max_session_bytes": 5242880, "keep_sessions": 250 } ``` Alle drei Schlüssel optional. **Vorrang: Umgebungsvariable > Datei > Formdefault.** Per-Checkout und gitignored wie `.wikitool-remotes.json`: die Zustimmung, Klartext-Prompts auf *diese* Platte zu schreiben, gehört dem Checkout, nicht dem Korpus. Nicht von `dist export` ausgeliefert, kein `.template`; `setup-instance.md` Schritt 10 schreibt sie aus der Antwort des Betreibers. Cache-Detail: `policy.resolve()` cacht nur die **dateisystemseitige Hälfte** (Stamp + Konfigurationsdatei), gekeyt auf den aufgelösten Root. Die Umgebungsvariablen werden bei jedem Aufruf frisch gelesen - sie kosten keine I/O, und ein Test, der eine davon mitten im Lauf umsetzt, muss den neuen Wert sofort sehen, nicht einen gecachten. Das war im Entwurf noch als "alles cachen, gekeyt auf den Root" formuliert und wäre so falsch gewesen. ### 4 - Versionsteil: `--minor` auf den laufenden Kandidaten Drop-in-Test bestanden, vorwärts wie rückwärts: additiv, kein Hand-Work, kein Migrationsskript; die alte Version ignoriert Datei und Variablen. `5.0.0-beta.8` → `5.0.0-beta.9`. Der Kandidat hatte die Grenze längst überschritten (Confidence-Entfernung), der `--minor` eskaliert ihn nicht (max-wins) und brauchte weder `--breaking` noch ein zweites Boundary-Gespräch. ## Akzeptanzkriterien - [x] In einem Baum **ohne** `.wikitool-release.json`, ohne gesetzte Umgebungsvariable, schreibt ein `wikitool`-Aufruf einen Trace. - [x] In einem Baum **mit** `.wikitool-release.json`, ohne Umgebungsvariable und ohne Opt-in, schreibt derselbe Aufruf nichts - und legt `reports/telemetry/` gar nicht erst an. Zusätzlich zum Unit-Test von Hand gegen einen frischen `dist export` durchgespielt. - [x] `WIKI_TRACE` überschreibt beide Defaults in **beide** Richtungen. - [x] `.wikitool-telemetry.json` mit `"enabled": true` schaltet in der ausgelieferten Instanz auf, ohne gesetzte Umgebungsvariable; `WIKI_TRACE=0` schlägt die Datei. - [x] Nach Erreichen des Byte-Deckels beendet ein weiterer Aufruf seine Arbeit unverändert mit Exit 0; die Trace-Datei überschreitet die Grenze um höchstens ein Event, und `telemetry.limit` steht dort genau einmal, auch nach weiteren Aufrufen. - [x] Die Durchsetzung löscht ausschließlich `trace.jsonl`/`.limit` der überzähligen Verzeichnisse. Der Test mit einer fremden Datei im gelöschten Verzeichnis prüft, dass diese byte-identisch stehen bleibt und das Verzeichnis nicht entfernt wird. - [x] Das Verzeichnis der laufenden Session wird von deren eigenem Retention-Lauf nie entfernt. - [x] `wikitool doctor` meldet den Telemetrie-Zustand: an/aus, warum, Menge gegen beide Deckel - und nie `FAIL`. - [x] Der MCP-Server startet in einer ausgelieferten Instanz ohne Opt-in ohne `WIKI_TRACE=0` und verweigert im Dev-Checkout weiterhin. `check_trace_destination` liest jetzt `policy.resolve()` statt `WIKI_TRACE` selbst - es gibt genau eine Stelle, die den Zustand auflöst. - [x] `EVALS.md`, `INSTALL.md`, `INSTALL-MCP.md`, `reports/CONTRACT.md`, `tools/CONTRACT.md` beschreiben Default, Opt-in und beide Deckel; `docs verify` und `instructions verify` grün. **Nachgezogen in `42646d8`:** `README.md` § "Telemetry and evaluation" behauptete weiter "Every `wikitool` call appends an event" - in einer ausgelieferten Instanz ab jetzt falsch. Beim `stack-close`-Durchgang gefunden, nicht beim ersten Publish. - [x] Der neue Default ist im Testlauf festgenagelt, nicht geerbt: jeder Telemetrie-Test setzt die Installationsform explizit, die neuen Variablen stehen in `_WIKITOOL_ENV`, und `policy.reset_cache()` hängt in `hermetic_environment`. - [x] CI-Replay prüft, dass im exportierten Baum kein `reports/telemetry/` entsteht (`.gitea/workflows/ci.yml`, § "The distribution works as a fresh instance"). ## Was verifiziert wurde - `pytest`: 1118 Tests grün (vorher 1095, also 23 neue), zweimal gelaufen - normal und gegen eine leere Maschine nach `testing-conventions.md` Schritt 6, mit identischem Ergebnis. - `tools/wikitool docs verify` und `tools/wikitool instructions verify`: grün. - `tools/wikitool doctor`: neuer `telemetry`-Check meldet im Dev-Checkout "on (no .wikitool-release.json - dev checkout, default on); 231/250 session(s), 378.242 byte(s)". - Hand-Replay eines `dist export` in einen frischen Baum (Identität, Personalization, Conventions, `instructions sync`, `index rebuild`, `doctor`, `docs verify`, `instructions verify`, `lint --fail-on-error`): `doctor` meldet dort "off (.wikitool-release.json present - distributed instance, default off)", und `reports/telemetry/` existiert nach dem gesamten Durchlauf nicht. - CI: Runs 228 und 229 auf `82a22ea`, Run 230 auf `42646d8`, dazu der Nightly-Lauf 231 - alle vier `success`, inklusive des `setup-instance.md`-Replays gegen einen frischen `dist export`. Ein Nebenbefund ohne Handlungsbedarf: `pytest --cov` lässt 271 unabhängige Tests (`test_xref.py`, `test_type_resolver.py`, `test_types_cmd.py`, …) fehlschlagen, die ohne `--cov` grün sind. Bestand schon vor dieser Änderung und ist von ihr unabhängig - eigenes Issue wert, falls die Coverage-Zahl je gebraucht wird. ## Berührte Stellen `tools/chemenu/telemetry/policy.py` (neu), `tools/chemenu/telemetry/writer.py`, `tools/chemenu/telemetry/schema.py`, `tools/chemenu/commands/doctor.py`, `tools/chemenu/mcp/server.py`, `tools/chemenu/config.py`, `tools/chemenu/version.py`, `.gitignore`, `.gitea/workflows/ci.yml`, `tools/chemenu/tests/conftest.py`, `tools/chemenu/tests/test_telemetry_policy.py` (neu), `test_telemetry_emit.py`, `test_doctor.py`, `test_mcp_server.py`, `EVALS.md`, `README.md`, `INSTALL.md`, `INSTALL-MCP.md`, `reports/CONTRACT.md`, `tools/CONTRACT.md`, `instructions/setup-instance.md`, `CHANGES.md`, `VERSION`. Nicht berührt: `dist_cmd.ROOT_FILES` - die Konfigurationsdatei ist per-Checkout und wird nicht ausgeliefert.
torben added the prio/plannedsize/Larea/distributionkind/decision labels 2026-09-04 18:57:02 +00:00
torben changed title from Telemetrie-Default nach Installationsform: Tarball opt-in, Clone opt-out - plus eine Mengenbegrenzung, deren Metrik noch offen ist to Telemetrie-Default nach Installationsform: Tarball opt-in, Clone opt-out - plus Byte-Deckel je Session und Retention ueber die Anzahl der Session-Verzeichnisse 2026-09-10 20:40:01 +00:00
torben added size/Mkind/build and removed size/Lkind/decision labels 2026-09-10 20:40:02 +00:00
Author
Owner

Changelog: Alle vier Entscheidungen getroffen, der Abschnitt "Zu entscheiden" ist durch "Entscheidungen" plus einen Umsetzungsplan ersetzt. Gewählt: (1) .wikitool-release.json als Marker, gegen config.ROOT aufgelöst; (2) Byte-Deckel je Session-Trace (5 MiB) und Retention über die Anzahl der Session-Verzeichnisse (250), Verhalten am Limit = Stopp plus genau ein telemetry.limit-Event, Rotation und Alters-Retention verworfen; (3) Opt-in in .wikitool-telemetry.json, per-Checkout und gitignored, Vorrang Env > Datei > Formdefault, im Setup-Interview gefragt; (4) --minor auf den laufenden 5.0.0-Kandidaten, Drop-in-Test in beide Richtungen bestanden.

Der Retention-Default 250 liegt bewusst über dem heutigen Bestand dieses Checkouts (229 Session-Verzeichnisse, 1,9 MB): der Deckel greift in Zukunft, der Altbestand wird nicht angefasst.

Neu unter den Akzeptanzkriterien: das Opt-in per Datei, die Nicht-Löschung des eigenen Session-Verzeichnisses, doctor meldet nie FAIL, genau eine Stelle im Code löst den Zustand auf (mcp/server.py liest WIKI_TRACE heute selbst — Invariante 8), und der CI-Replay als End-to-End-Prüfung, dass im exportierten Baum kein reports/telemetry/ entsteht. Titel nachgezogen ("Metrik noch offen" gilt nicht mehr), kind/decisionkind/build, size/Lsize/M: die offenen Designfragen vor dem ersten Commit sind weg, es bleiben mehrere Dateien plus eigener Testaufwand.

Noch nichts implementiert — die Sitzung hält vor der Umsetzung an.

**Changelog:** Alle vier Entscheidungen getroffen, der Abschnitt "Zu entscheiden" ist durch "Entscheidungen" plus einen Umsetzungsplan ersetzt. Gewählt: (1) `.wikitool-release.json` als Marker, gegen `config.ROOT` aufgelöst; (2) Byte-Deckel je Session-Trace (5 MiB) **und** Retention über die Anzahl der Session-Verzeichnisse (250), Verhalten am Limit = Stopp plus genau ein `telemetry.limit`-Event, Rotation und Alters-Retention verworfen; (3) Opt-in in `.wikitool-telemetry.json`, per-Checkout und gitignored, Vorrang Env > Datei > Formdefault, im Setup-Interview gefragt; (4) `--minor` auf den laufenden 5.0.0-Kandidaten, Drop-in-Test in beide Richtungen bestanden. Der Retention-Default 250 liegt bewusst über dem heutigen Bestand dieses Checkouts (229 Session-Verzeichnisse, 1,9 MB): der Deckel greift in Zukunft, der Altbestand wird nicht angefasst. Neu unter den Akzeptanzkriterien: das Opt-in per Datei, die Nicht-Löschung des eigenen Session-Verzeichnisses, `doctor` meldet nie `FAIL`, genau eine Stelle im Code löst den Zustand auf (`mcp/server.py` liest `WIKI_TRACE` heute selbst — Invariante 8), und der CI-Replay als End-to-End-Prüfung, dass im exportierten Baum kein `reports/telemetry/` entsteht. Titel nachgezogen ("Metrik noch offen" gilt nicht mehr), `kind/decision` → `kind/build`, `size/L` → `size/M`: die offenen Designfragen vor dem ersten Commit sind weg, es bleiben mehrere Dateien plus eigener Testaufwand. Noch nichts implementiert — die Sitzung hält vor der Umsetzung an.
Author
Owner

Implemented and tested, ready to publish (waiting on the Mass-Update Gate, not yet pushed):

  • tools/chemenu/telemetry/policy.py (new): resolves enabled, max_session_bytes, keep_sessions in one place, cached per resolved root (filesystem-derived half only; WIKI_TRACE/WIKI_TRACE_MAX_SESSION_BYTES/WIKI_TRACE_KEEP_SESSIONS stay live).
  • writer.py: enabled() delegates to policy; emit() enforces the byte cap (_mark_limit_once, single-writer .limit sentinel) and the retention pass (_enforce_retention, reserves the about-to-be-created session's own slot so the count actually converges to keep instead of drifting to keep+1).
  • mcp/server.py: check_trace_destination now reads the same policy instead of WIKI_TRACE a second time (was an invariant-8 violation).
  • doctor.py: new telemetry check, never FAIL.
  • config.py: RELEASE_STAMP_FILENAME moved here (canonical, stdlib-safe for the telemetry package); version.py re-exports it. New TELEMETRY_FILENAME = ".wikitool-telemetry.json".
  • Docs: EVALS.md, INSTALL.md, INSTALL-MCP.md, reports/CONTRACT.md, tools/CONTRACT.md, instructions/setup-instance.md (new step 10, renumbered 10-14 → 11-15).
  • CI: asserts reports/telemetry/ does not exist after the fresh-instance replay.
  • Tests: test_telemetry_policy.py (new), extended test_telemetry_emit.py, test_doctor.py, test_mcp_server.py. Full suite 1118 passed, twice (normal + empty-machine per testing-conventions.md). docs verify/instructions verify clean. Manually replayed a dist export fresh-instance setup: telemetry defaults off, reports/telemetry/ never created.
  • Version bumped --minor (5.0.0-beta.85.0.0-beta.9, drop-in both directions, no new boundary crossing) with CHANGES.md prose.

tools/wikitool publish hit the Mass-Update Gate as expected (22 files) - session is showing the user the gate output now for --confirm.

Implemented and tested, ready to publish (waiting on the Mass-Update Gate, not yet pushed): - `tools/chemenu/telemetry/policy.py` (new): resolves `enabled`, `max_session_bytes`, `keep_sessions` in one place, cached per resolved root (filesystem-derived half only; `WIKI_TRACE`/`WIKI_TRACE_MAX_SESSION_BYTES`/`WIKI_TRACE_KEEP_SESSIONS` stay live). - `writer.py`: `enabled()` delegates to policy; `emit()` enforces the byte cap (`_mark_limit_once`, single-writer `.limit` sentinel) and the retention pass (`_enforce_retention`, reserves the about-to-be-created session's own slot so the count actually converges to `keep` instead of drifting to `keep+1`). - `mcp/server.py`: `check_trace_destination` now reads the same policy instead of `WIKI_TRACE` a second time (was an invariant-8 violation). - `doctor.py`: new `telemetry` check, never `FAIL`. - `config.py`: `RELEASE_STAMP_FILENAME` moved here (canonical, stdlib-safe for the telemetry package); `version.py` re-exports it. New `TELEMETRY_FILENAME = ".wikitool-telemetry.json"`. - Docs: `EVALS.md`, `INSTALL.md`, `INSTALL-MCP.md`, `reports/CONTRACT.md`, `tools/CONTRACT.md`, `instructions/setup-instance.md` (new step 10, renumbered 10-14 → 11-15). - CI: asserts `reports/telemetry/` does not exist after the fresh-instance replay. - Tests: `test_telemetry_policy.py` (new), extended `test_telemetry_emit.py`, `test_doctor.py`, `test_mcp_server.py`. Full suite 1118 passed, twice (normal + empty-machine per testing-conventions.md). `docs verify`/`instructions verify` clean. Manually replayed a `dist export` fresh-instance setup: telemetry defaults off, `reports/telemetry/` never created. - Version bumped `--minor` (`5.0.0-beta.8` → `5.0.0-beta.9`, drop-in both directions, no new boundary crossing) with `CHANGES.md` prose. `tools/wikitool publish` hit the Mass-Update Gate as expected (22 files) - session is showing the user the gate output now for `--confirm`.
Author
Owner

Changelog: Geschlossen. Body auf den Endzustand umgeschrieben - "Umsetzungsplan" ist raus (ausgeführt), alle dreizehn Akzeptanzkriterien abgehakt, dazu ein Abschnitt "Was verifiziert wurde" mit den vier grünen CI-Läufen (228/229 auf 82a22ea, 230 auf 42646d8, Nightly 231) und dem Hand-Replay gegen einen frischen dist export.

Zwei Abweichungen vom geplanten Entwurf sind im Body dokumentiert, weil sie beide erst beim Bauen auffielen: die Retention reserviert den Platz der entstehenden Session (keep - 1) statt auf keep zu trimmen - die naive Variante pendelt dauerhaft bei keep + 1, weil jeder Lauf nur räumt, was der vorige übrig ließ; und policy.resolve() cacht nur die dateisystemseitige Hälfte, nicht die Umgebungsvariablen, die sonst nach einem monkeypatch im selben Prozess eingefroren wären. Dazu ein Umbau, den der Plan nicht vorsah: RELEASE_STAMP_FILENAME wohnt jetzt in config.py und wird von version.py re-exportiert, weil chemenu.telemetry ohne venv importierbar bleiben muss und eine zweite Kopie des Dateinamens Invariante 8 verletzt hätte.

Nachzügler in 42646d8: README.md § "Telemetry and evaluation" behauptete weiter "Every wikitool call appends an event". Beim stack-close-Durchgang gefunden, nicht beim ersten Publish - prosa-only, daher ohne Bump.

Modelle dieser Sitzung: Design/Entscheidungen/Versionsteil, mechanische Mitte (Code, Tests, Bump) und diese Abschlussphase liefen alle drei auf Opus 5; der in stack-dev Schritt 3 angebotene Wechsel auf Sonnet wurde nicht gezogen.

**Changelog:** Geschlossen. Body auf den Endzustand umgeschrieben - "Umsetzungsplan" ist raus (ausgeführt), alle dreizehn Akzeptanzkriterien abgehakt, dazu ein Abschnitt "Was verifiziert wurde" mit den vier grünen CI-Läufen (228/229 auf `82a22ea`, 230 auf `42646d8`, Nightly 231) und dem Hand-Replay gegen einen frischen `dist export`. Zwei Abweichungen vom geplanten Entwurf sind im Body dokumentiert, weil sie beide erst beim Bauen auffielen: die Retention **reserviert den Platz der entstehenden Session** (`keep - 1`) statt auf `keep` zu trimmen - die naive Variante pendelt dauerhaft bei `keep + 1`, weil jeder Lauf nur räumt, was der vorige übrig ließ; und `policy.resolve()` cacht nur die dateisystemseitige Hälfte, nicht die Umgebungsvariablen, die sonst nach einem `monkeypatch` im selben Prozess eingefroren wären. Dazu ein Umbau, den der Plan nicht vorsah: `RELEASE_STAMP_FILENAME` wohnt jetzt in `config.py` und wird von `version.py` re-exportiert, weil `chemenu.telemetry` ohne venv importierbar bleiben muss und eine zweite Kopie des Dateinamens Invariante 8 verletzt hätte. Nachzügler in `42646d8`: `README.md` § "Telemetry and evaluation" behauptete weiter "Every `wikitool` call appends an event". Beim `stack-close`-Durchgang gefunden, nicht beim ersten Publish - prosa-only, daher ohne Bump. Modelle dieser Sitzung: Design/Entscheidungen/Versionsteil, mechanische Mitte (Code, Tests, Bump) und diese Abschlussphase liefen alle drei auf Opus 5; der in `stack-dev` Schritt 3 angebotene Wechsel auf Sonnet wurde nicht gezogen.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: torben/chemenu#55