18ae28f918
Chemenu kompiliert Rohnotizen zu einem verlinkten, quellengebundenen Wiki: raw/ -> types/ + tools/ -> kb/ -> reports/. Was mechanisch ist, macht tools/wikitool; was Urteil braucht, macht ein Agent unter Contracts, deren Grenzen in Code durchgesetzt sind statt im Prompt. Dieser Commit ist der Startpunkt der oeffentlichen Historie. Die vorherige Entwicklung fand in einer privaten Instanz statt und ist nicht Teil dieses Repositorys; ihre Erzaehlung steht vollstaendig in CHANGES.md, das mit 44 Eintraegen von 0.1.0 bis 2.1.0 erhalten geblieben ist. Der mitgelieferte Korpus ist ein Testbett und eine Demo: 170 Seiten ueber den Stack selbst - Gates, Lint, Versionierung, Suche, das Wiki-Muster. Er dokumentiert das Werkzeug mit den eigenen Mitteln des Werkzeugs. Lizenz: AGPL-3.0 fuer den Stack (tools/, types/), CC-BY-4.0 fuer die Inhalte. Die Grenze zwischen beiden ist der Dateiplan, den dist export berechnet - siehe NOTICE.
145 lines
8.9 KiB
Markdown
145 lines
8.9 KiB
Markdown
---
|
|
type: types/source.md
|
|
source_type: notes
|
|
author: Claude Code (claude-opus-5)
|
|
raw_files: [raw/notes/Conversation Transcript - Write-Once Frontmatter Fields and touch --set Session 2026-08-31.md]
|
|
source_language: en
|
|
date: 2026-08-31
|
|
tags: [wikitool, cli, frontmatter, touch, schema, idempotenz, gitea]
|
|
entities: [wikitool, Chemenu, Gitea, AGENTS.md]
|
|
concepts: [Write-Once Frontmatter Fields, Denylist over Allowlist]
|
|
summary: 'Sitzung, die write-once-Frontmatterfelder reparierbar macht: touch bekommt --set/--add/--remove ueber eine Denylist statt einer Allowlist, ein idempotentes --remove und einen bewusst engen Scope (Stack 1.4.0, Gitea-Issue #14)'
|
|
---
|
|
# Source: Conversation - Write-Once Frontmatter Fields and touch --set Session 2026-08-31
|
|
|
|
**Autor:** Claude Code (claude-opus-5)
|
|
**Datum:** 2026-08-31
|
|
**Raw-Dateien:** raw/notes/Conversation Transcript - Write-Once Frontmatter Fields and touch --set Session 2026-08-31.md
|
|
**Typ:** Notes
|
|
|
|
## Zusammenfassung
|
|
|
|
Das Transkript ist eine zusammenfassende Rekonstruktion der Sitzung, kein wörtliches Protokoll.
|
|
Die zitierten Befehlsausgaben sind echt, Torbens drei Entwurfsentscheidungen sind samt der ihm
|
|
vorgelegten Optionen festgehalten, die Begründungen des Assistenten sind verdichtet. Es ist
|
|
eines von zwei Transkripten dieses Sitzungsabschnitts; das zweite behandelt die Zählregel des
|
|
Mass-Update Gate und die gemessene Kalibrierung des Iteration Budget und wird getrennt
|
|
eingelesen.
|
|
|
|
Gegenstand ist Gitea-Issue #14, ausgeliefert als `touch --set/--add/--remove` in Stack-Version
|
|
`1.4.0` (Commit `dbe2f73`, 9 Dateien, 674 Tests grün). Der Defekt, den es schließt: ein Feld,
|
|
das `new` einmal geschrieben hat, war danach nicht mehr erreichbar. `touch` kannte nur die
|
|
Felder `modified`, `summary`, `provenance` und `confidence_base`; Handeditierung ist genau das,
|
|
was der Stack verhindern soll; Löschen und Neuanlegen zerreißt jede bestehende Referenz auf die
|
|
Seite; und `new` ist nicht idempotent, das Zeitfenster für den richtigen Wert war also genau
|
|
einen Befehl breit.
|
|
|
|
Den bleibenden Wert der Sitzung tragen drei Entscheidungen, die Torben mit ausgewiesenen
|
|
Trade-offs vorgelegt wurden: Denylist statt Allowlist für die schreibbaren Felder, Ersetzen
|
|
plus `--add`/`--remove` für Listenfelder, und ein bewusst enger Auslieferungsschnitt, der
|
|
`raw rename` als Issue #16 abspaltet.
|
|
|
|
## Kernaussagen
|
|
|
|
- **Die Vorarbeit lag schon im Code.** `validate_fields()` in `touch.py` validiert **pro Feld**
|
|
statt pro Dokument - genau die Form, die ein `--set` braucht. Der Grund steht im Docstring:
|
|
eine Validierung über das ganze Dokument würde sich weigern, `modified:` auf einer Seite zu
|
|
bumpen, die aus einem unbeteiligten Grund ungültig ist, also auf der Seite, die Wartung am
|
|
dringendsten braucht.
|
|
- **Entscheidung 1 - Denylist statt Allowlist.** Schreibbar ist alles, was das Schema für den
|
|
Seitentyp deklariert, abzüglich einer kurzen begründeten Sperrliste. Das Argument, das den
|
|
Ausschlag gab: eine gepflegte Allowlist ist eine zweite Kopie des Schemas, und die Kopie ist
|
|
die Seite, die driftet - Invariante 8 aus `AGENTS.md`, angewandt auf eine Konstante. Der
|
|
Preis der Allowlist wäre gewesen, dass jedes neue Schema-Feld eine Codeänderung braucht.
|
|
- **Gesperrt sind vier Gruppen, jede mit dem Befehl benannt, dem das Feld gehört:** `type:`
|
|
ändert Schema *und* Verzeichnis der Seite und gehört nach `page-lifecycle.md`; `confidence:`
|
|
ist aus `confidence_base` abgeleitet und nicht autorisiert; `related:`, `sources:`,
|
|
`entities:` und `concepts:` gehören `xref`, weil ein blanker Frontmatter-Schreibvorgang die
|
|
Gegenrichtung und die Body-Bullets stehen ließe.
|
|
- **Entscheidung 2 - Ersetzen plus `--add`/`--remove`.** Reines Ersetzen wäre eine Regel
|
|
gewesen, hätte aber verlangt, für ein einzelnes Tag die ganze Liste zu nennen. Der Preis der
|
|
gewählten Variante sind drei Optionen statt einer.
|
|
- **Die Teilfrage, was `--remove` mit einem nicht vorhandenen Element tut, entschied der
|
|
Assistent selbst und wies das aus:** es gelingt und wird gemeldet. Idempotent wie
|
|
`xref remove`, weil ein Reparaturbefehl, der sich beim zweiten Lauf verweigert, nicht
|
|
skriptbar ist - aber nie stillschweigend, weil ein stiller No-op genauso aussieht wie eine
|
|
gelungene Entfernung, und genau so verbirgt sich ein vertippter Elementname.
|
|
- **Entscheidung 3 - enger Schnitt.** Nur `touch --set`; die Dateiverschiebung bleibt zweistufig
|
|
(`git mv`, dann `touch --set raw_files=…`). Die Alternative, `raw rename` mitzuliefern, hätte
|
|
Issue #14 vollständig geschlossen und den Zwischenzustand vermieden, zum Preis von `size/M`:
|
|
Rückwärtssuche über alle `raw_files:`-Referenzen, Verhalten bei mehreren Besitzern,
|
|
Contract-Zeilen für zwei Befehle. `raw rename` wurde Issue #16 (`prio/2`, `size/S`).
|
|
- **Zwei Kommandos, eine Implementierung.** `_coerce_set_value`, `_parse_set_fields` und
|
|
`_check_raw_files_exist` wanderten aus `new_page.py` nach `commands/_util.py` und verloren
|
|
ihren führenden Unterstrich. Ohne das hätte `touch --set` den Komma-Defekt aus Issue #12 am
|
|
ersten Tag geerbt; ein Test deckt genau diesen Fall ab, mit einer Rohdatei, deren Name ein
|
|
Komma enthält, referenziert über `\,` und als ein Pfad zurückgelesen.
|
|
- **Der Existenzcheck für `raw_files:` gilt auch für `touch`,** identisch zu dem, den `new`
|
|
ausführt. Er ist I/O und keine Datenform, also kann kein Schema ihn ausdrücken.
|
|
- **Die beiden Ablehnungen sind bewusst unterschiedlich formuliert.** Ein gesperrtes Feld ist
|
|
ein Routing-Problem, die Meldung nennt deshalb den zuständigen Befehl. Ein unbekanntes Feld
|
|
ist ein Tippfehler oder der falsche Seitentyp, die Meldung listet deshalb auf, welche Felder
|
|
die Seite tatsächlich hat - der Nutzwert liegt darin, zu erfahren, dass `tags` gemeint war.
|
|
- **Eine Falle im Testaufbau, einmal beseitigt.** `test_touch.py` rief den Typer-Callback direkt
|
|
mit vollständiger Argumentliste auf; drei neue Optionen brachen sieben Aufrufstellen mit
|
|
`TypeError: 'OptionInfo' object is not iterable`, weil ein direkt aufgerufener Callback für
|
|
jedes ausgelassene Argument ein `OptionInfo`-Objekt bekommt. Die Tests laufen jetzt über einen
|
|
`_touch(**overrides)`-Helper, der jede Option belegt; die nächste Option kostet eine Zeile
|
|
statt sieben.
|
|
- **Der Beleg am realen Korpus:** [[Diff-Reviewable Agent Edits]] war Stunden zuvor von einem
|
|
Ingest angelegt worden, dessen `--set tags=`-Wert ein Komma am Ende trug, worauf alles hinter
|
|
dem Trennzeichen verlorenging. Der Subagent hatte alle drei Auswege korrekt geprüft und
|
|
verworfen - `touch` konnte `tags:` nicht setzen, Handeditierung kommt Invariante 1 zu nahe,
|
|
`rm` plus `new` hätte die `concepts:`-Referenz der Source-Seite zerrissen. Die Seite behielt
|
|
`[agent-workflow]` dauerhaft, wegen eines Kommas. Mit `touch --add` trägt sie jetzt
|
|
`[agent-workflow, context-engineering, tooling]`.
|
|
- **Der zweite Aufruf desselben `--add` meldete "already up to date; nothing to change",** der
|
|
`--remove` eines nicht vorhandenen Elements meldete "not present, nothing removed" und endete
|
|
ebenfalls erfolgreich - die zugesagte Idempotenz, an der Kommandozeile gezeigt.
|
|
- **Dass dies eine Rate und kein Einzelfall war:** drei Fehlschläge in drei aufeinanderfolgenden
|
|
Ingests desselben Tages, an zwei verschiedenen Feldern, von drei verschiedenen Agenten. Einer
|
|
davon war ein nachgestelltes Komma.
|
|
- **Ergebnis:** `1.4.0` als MINOR (neue Fähigkeit, rückwärtskompatibel), Commit `dbe2f73` über
|
|
9 Dateien, 674 Tests grün in der normalen und in der gehärteten Umgebung, Issue #14
|
|
geschlossen mit den drei Entscheidungen im Protokoll, Issue #16 eröffnet.
|
|
|
|
## Aufgaben
|
|
|
|
- [ ] Gitea-Issue #16 - `raw rename`, das `git mv` und jede referenzierende Source-Seite in
|
|
einem Schritt erledigt (`prio/2`, `size/S`)
|
|
|
|
## Nicht übernommen
|
|
|
|
- **Die Modul- und Funktionsnamen der Testumbauten** über den `_touch`-Helper hinaus. Der
|
|
Umbau selbst ist eine dauerhafte Aussage über die Testschnittstelle, die einzelnen sieben
|
|
Aufrufstellen sind es nicht.
|
|
- **Der vollständige Wortlaut der beiden Fehlermeldungen.** Übernommen ist die Entwurfsregel
|
|
dahinter - Routing-Problem nennt den Befehl, Tippfehler nennt die vorhandenen Felder -, weil
|
|
der Wortlaut sich ändern kann und die Regel nicht.
|
|
- **Das Schwestertranskript desselben Sitzungsabschnitts** (Zählregel des Mass-Update Gate,
|
|
gemessene Kalibrierung des Iteration Budget). Es wird getrennt eingelesen und bekommt eine
|
|
eigene Source-Seite; hier stünde es unbelegt.
|
|
- **Die Einzelheiten der 674 Tests.** Übernommen sind die Gesamtzahl und die Testfalle, die
|
|
sich daran zeigte.
|
|
- **Issue #14 als eigene Seite.** Es ist geschlossen, und sein Ergebnis steht auf [[wikitool]],
|
|
[[Write-Once Frontmatter Fields]] und [[Detect-Repair Asymmetry]].
|
|
|
|
## Verwandte Entities
|
|
|
|
- [[wikitool]]
|
|
- [[Chemenu]]
|
|
- [[Gitea]]
|
|
- [[AGENTS.md]]
|
|
|
|
## Verwandte Concepts
|
|
|
|
- [[Denylist over Allowlist]]
|
|
- [[Detect-Repair Asymmetry]]
|
|
- [[Diff-Reviewable Agent Edits]]
|
|
|
|
## Beziehungen
|
|
|
|
|
|
## Siehe auch
|
|
|