Beim Zurückbenennen der Rohdatei aus #12 (5426a6e) musste raw_files: der Source-Seite von Hand korrigiert werden. Es gibt kein wikitool-Kommando dafür.
touch schreibt die Felder, die die Seite selbst beschreiben (modified:, summary:, provenance:, confidence_base:) — raw_files: gehört nicht dazu.
xref add/remove/link-source fassen die Page-Ref-Arrays an (related:, sources:, entities:, concepts:). raw_files: ist keins davon: es zeigt auf einen Pfad, nicht auf einen Seitentitel.
new source --set raw_files=… schreibt das Feld einmal, beim Anlegen. Danach nie wieder.
Invariante 1 verbietet das Hand-Editieren hier nicht — ihre Liste nennt Katalog, log.md, provenance.md, die Skill-Verzeichnisse, die beiden JSON-Dateien und die Page-Ref-Arrays, und raw_files: steht in keiner davon. Trotzdem widerspricht es dem Kernprinzip: „anything mechanical is done by tools/wikitool, never by hand". Eine Pfadkorrektur in einem Frontmatter-Array ist genau das.
Wann das auftritt
Nicht nur beim Reparieren eines Bugs. Jedes Mal, wenn eine Rohdatei ihren Pfad ändert:
Umbenennung, wie hier
Umsortieren zwischen den Verzeichnissen aus raw/CONTRACT.md (notes/ → documents/, wenn sich herausstellt, dass es ein Dokument war)
ein Tree-Ingest, der raw/-Struktur nachträglich umbaut (siehe instructions/ingest-large-tree.md)
eine Rohdatei nachträglich einer bestehenden Source-Seite zuschlagen, statt eine zweite anzulegen
lint und sources coveragemelden den gebrochenen Zustand zuverlässig (Broken raw_files references), aber nichts kann ihn beheben. Das ist die Asymmetrie, die dieses Issue schließen soll: eine Prüfung ohne zugehörige Reparatur schickt den Agenten in genau die Hand-Edits, gegen die der Rest des Stacks gebaut ist.
Verschiebt das Kommando die Datei selbst, oder erwartet es sie bereits am Ziel? Für Pages löst page-lifecycle.md genau diese Frage über wikitool rename, das beides tut. Analog wäre ein raw rename <alt> <neu>, das git mvund jede referenzierende Source-Seite anfasst, die geschlossenere Form — und die einzige, bei der der Zwischenzustand „Datei weg, Referenz zeigt ins Leere" nie entsteht.
Prüfen, dass der Zielpfad existiert und unter raw/ liegt (_check_raw_files_exist kann das schon).
Verhalten, wenn mehrere Source-Seiten dieselbe Rohdatei beanspruchen — lint führt das als eigenen Befund (duplicate_raw_file_owners), das Kommando sollte es nicht stillschweigend erzeugen.
sources rebuild-index danach automatisch, oder dem Aufrufer überlassen? Die anderen Kommandos überlassen es ihm.
Nicht Teil davon
Rohdateien mit einem raw-Unterbefehl generell zu verwalten. raw/ ist unveränderlich; das hier ist der eng begrenzte Fall, dass ein Pfad sich ändert, während der Inhalt derselbe bleibt.
Akzeptanzkriterien
Ein Kommando korrigiert raw_files: einer bestehenden Seite, ohne dass jemand Frontmatter anfasst.
Der Fall aus 5426a6e — Rohdatei umbenannt, eine Source-Seite referenziert sie — ist der Test.
Zeile in tools/CONTRACT.md für Kommando und Fehlerkontrakt; docs verify erzwingt beide Richtungen.
instructions/page-lifecycle.md erwähnt den Rohdatei-Fall, oder sagt ausdrücklich, dass er woanders steht. Heute schweigt es dazu, und sein Scope-Abschnitt grenzt nur gegen Contracts/Instructions/Type-Specs ab.
Changelog-Eintrag, MINOR — neue Fähigkeit, rückwärtskompatibel.
Hinweis
Der Anlassfall selbst ist erledigt und braucht keine Nacharbeit: sources coverage meldet 0 unabgedeckt und 0 gebrochene Referenzen, lint ist sauber über 255 Seiten. Dieses Issue ist die Lücke im Werkzeug, nicht der Schaden.
## Der Anlass
Beim Zurückbenennen der Rohdatei aus #12 (`5426a6e`) musste `raw_files:` der Source-Seite von Hand korrigiert werden. Es gibt kein `wikitool`-Kommando dafür.
- `touch` schreibt die Felder, die die Seite *selbst* beschreiben (`modified:`, `summary:`, `provenance:`, `confidence_base:`) — `raw_files:` gehört nicht dazu.
- `xref add`/`remove`/`link-source` fassen die Page-Ref-Arrays an (`related:`, `sources:`, `entities:`, `concepts:`). `raw_files:` ist keins davon: es zeigt auf einen Pfad, nicht auf einen Seitentitel.
- `new source --set raw_files=…` schreibt das Feld **einmal**, beim Anlegen. Danach nie wieder.
Invariante 1 verbietet das Hand-Editieren hier nicht — ihre Liste nennt Katalog, `log.md`, `provenance.md`, die Skill-Verzeichnisse, die beiden JSON-Dateien und die Page-Ref-Arrays, und `raw_files:` steht in keiner davon. Trotzdem widerspricht es dem Kernprinzip: „anything mechanical is done by `tools/wikitool`, never by hand". Eine Pfadkorrektur in einem Frontmatter-Array ist genau das.
## Wann das auftritt
Nicht nur beim Reparieren eines Bugs. Jedes Mal, wenn eine Rohdatei ihren Pfad ändert:
- Umbenennung, wie hier
- Umsortieren zwischen den Verzeichnissen aus `raw/CONTRACT.md` (`notes/` → `documents/`, wenn sich herausstellt, dass es ein Dokument war)
- ein Tree-Ingest, der `raw/`-Struktur nachträglich umbaut (siehe `instructions/ingest-large-tree.md`)
- eine Rohdatei nachträglich einer bestehenden Source-Seite zuschlagen, statt eine zweite anzulegen
`lint` und `sources coverage` **melden** den gebrochenen Zustand zuverlässig (`Broken raw_files references`), aber nichts kann ihn beheben. Das ist die Asymmetrie, die dieses Issue schließen soll: eine Prüfung ohne zugehörige Reparatur schickt den Agenten in genau die Hand-Edits, gegen die der Rest des Stacks gebaut ist.
## Vorschlag für den Schnitt
`wikitool sources relink --page "<Titel>" --from <alter-pfad> --to <neuer-pfad>` — und/oder ein additives `--add`/`--remove`.
Offene Fragen für die Umsetzung:
- **Verschiebt das Kommando die Datei selbst, oder erwartet es sie bereits am Ziel?** Für Pages löst `page-lifecycle.md` genau diese Frage über `wikitool rename`, das beides tut. Analog wäre ein `raw rename <alt> <neu>`, das `git mv` **und** jede referenzierende Source-Seite anfasst, die geschlossenere Form — und die einzige, bei der der Zwischenzustand „Datei weg, Referenz zeigt ins Leere" nie entsteht.
- Prüfen, dass der Zielpfad existiert und unter `raw/` liegt (`_check_raw_files_exist` kann das schon).
- Verhalten, wenn mehrere Source-Seiten dieselbe Rohdatei beanspruchen — `lint` führt das als eigenen Befund (`duplicate_raw_file_owners`), das Kommando sollte es nicht stillschweigend erzeugen.
- `sources rebuild-index` danach automatisch, oder dem Aufrufer überlassen? Die anderen Kommandos überlassen es ihm.
## Nicht Teil davon
Rohdateien mit einem `raw`-Unterbefehl generell zu verwalten. `raw/` ist unveränderlich; das hier ist der eng begrenzte Fall, dass ein *Pfad* sich ändert, während der *Inhalt* derselbe bleibt.
## Akzeptanzkriterien
- [ ] Ein Kommando korrigiert `raw_files:` einer bestehenden Seite, ohne dass jemand Frontmatter anfasst.
- [ ] Der Fall aus `5426a6e` — Rohdatei umbenannt, eine Source-Seite referenziert sie — ist der Test.
- [ ] Zeile in `tools/CONTRACT.md` für Kommando **und** Fehlerkontrakt; `docs verify` erzwingt beide Richtungen.
- [ ] `instructions/page-lifecycle.md` erwähnt den Rohdatei-Fall, oder sagt ausdrücklich, dass er woanders steht. Heute schweigt es dazu, und sein Scope-Abschnitt grenzt nur gegen Contracts/Instructions/Type-Specs ab.
- [ ] Changelog-Eintrag, **MINOR** — neue Fähigkeit, rückwärtskompatibel.
## Hinweis
Der Anlassfall selbst ist erledigt und braucht keine Nacharbeit: `sources coverage` meldet 0 unabgedeckt und 0 gebrochene Referenzen, `lint` ist sauber über 255 Seiten. Dieses Issue ist die Lücke im Werkzeug, nicht der Schaden.
Zweiter Fall derselben Lücke, anderes Feld: tags:.
Beim Ingest des Transkripts zu diesem Fix (Commit 8764799) musste tags: auf zwei neu angelegten Seiten von Hand gesetzt werden. new nimmt das Feld nur bei der Erstellung entgegen; wird es dort vergessen, gibt es keinen Weg zurück — touch schreibt nur die Felder, die die Seite selbst beschreiben (modified:, summary:, provenance:, confidence_base:), und tags: gehört nicht dazu.
Wie raw_files: fällt tags: nicht unter Invariante 1 — die Liste dort nennt Katalog, log.md, provenance.md, die Skill-Verzeichnisse, die beiden JSON-Dateien und die Page-Ref-Arrays. Und wie bei raw_files: ist die Handeditierung trotzdem der einzige Weg.
Das verschiebt den Zuschnitt dieses Issues. Es geht nicht um ein Kommando für raw_files:, sondern um eine Klasse: Frontmatter-Felder, die bei new schreibbar sind und danach nie wieder. Ein sources relink würde raw_files: lösen und tags: offen lassen, und beim nächsten Feld stünde derselbe Kommentar hier.
Zwei Zuschnitte, die beide mehr treffen als ein Einzelkommando:
touch --set field=value mit derselben Coercion und Schema-Validierung, die new --set schon hat (_coerce_set_value + _validate_or_fail). Deckt tags:, raw_files: und jedes künftige skalare oder Array-Feld ab, das kein Page-Ref-Array ist. Die Page-Ref-Arrays blieben ausdrücklich bei xref, weil dort die Gegenrichtung mitgepflegt wird.
raw rename <alt> <neu> zusätzlich, für den Fall, dass die Datei sich bewegt — git mv plus jede referenzierende Source-Seite in einem Schritt, damit der Zwischenzustand „Datei weg, Referenz zeigt ins Leere" nie entsteht. Das ist der Teil, den (1) nicht abdeckt.
Vorschlag: Titel und Scope auf (1) erweitern, (2) als zweiten Akzeptanzpunkt behalten.
Verwandt: der Ingest hat das Muster als Concept-Seite Detect-Repair Asymmetry abgelegt — ein Check meldet einen Defekt zuverlässig (lint, sources coverage), kein Befehl behebt ihn, also bleibt die Handeditierung. Das ist die Verallgemeinerung dieses Issues.
**Zweiter Fall derselben Lücke, anderes Feld: `tags:`.**
Beim Ingest des Transkripts zu diesem Fix (Commit `8764799`) musste `tags:` auf zwei neu angelegten Seiten von Hand gesetzt werden. `new` nimmt das Feld nur bei der Erstellung entgegen; wird es dort vergessen, gibt es keinen Weg zurück — `touch` schreibt nur die Felder, die die Seite selbst beschreiben (`modified:`, `summary:`, `provenance:`, `confidence_base:`), und `tags:` gehört nicht dazu.
Wie `raw_files:` fällt `tags:` nicht unter Invariante 1 — die Liste dort nennt Katalog, `log.md`, `provenance.md`, die Skill-Verzeichnisse, die beiden JSON-Dateien und die Page-Ref-Arrays. Und wie bei `raw_files:` ist die Handeditierung trotzdem der einzige Weg.
Das verschiebt den Zuschnitt dieses Issues. Es geht nicht um ein Kommando für `raw_files:`, sondern um eine **Klasse**: Frontmatter-Felder, die bei `new` schreibbar sind und danach nie wieder. Ein `sources relink` würde `raw_files:` lösen und `tags:` offen lassen, und beim nächsten Feld stünde derselbe Kommentar hier.
Zwei Zuschnitte, die beide mehr treffen als ein Einzelkommando:
1. **`touch --set field=value`** mit derselben Coercion und Schema-Validierung, die `new --set` schon hat (`_coerce_set_value` + `_validate_or_fail`). Deckt `tags:`, `raw_files:` und jedes künftige skalare oder Array-Feld ab, das kein Page-Ref-Array ist. Die Page-Ref-Arrays blieben ausdrücklich bei `xref`, weil dort die Gegenrichtung mitgepflegt wird.
2. **`raw rename <alt> <neu>`** zusätzlich, für den Fall, dass die *Datei* sich bewegt — `git mv` plus jede referenzierende Source-Seite in einem Schritt, damit der Zwischenzustand „Datei weg, Referenz zeigt ins Leere" nie entsteht. Das ist der Teil, den (1) nicht abdeckt.
Vorschlag: Titel und Scope auf (1) erweitern, (2) als zweiten Akzeptanzpunkt behalten.
Verwandt: der Ingest hat das Muster als Concept-Seite `Detect-Repair Asymmetry` abgelegt — ein Check meldet einen Defekt zuverlässig (`lint`, `sources coverage`), kein Befehl behebt ihn, also bleibt die Handeditierung. Das ist die Verallgemeinerung dieses Issues.
Beim Ingest des dritten Transkripts (Commit 8d47e7e) hatte ein --set tags=-Aufruf ein Komma am Ende; alles nach dem Trennzeichen fiel weg. kb/concepts/Diff-Reviewable Agent Edits.md trägt deshalb nur [agent-workflow] statt der vorgesehenen Liste.
Interessant ist nicht der Tippfehler, sondern dass es keinen Weg zurück gab. Der Agent hat die drei Auswege korrekt durchgespielt und alle drei verworfen:
touch kann tags: nicht setzen.
Ein Hand-Edit am Frontmatter kommt Invariante 1 zu nah.
rm + new hätte die bereits geschriebene concepts:-Referenz der Source-Seite gebrochen.
Also blieb: so lassen. Der Tag ist gültig, die Seite über Titel, Summary und drei Backlinks auffindbar — aber die Kategorisierung ist dauerhaft dünner als beabsichtigt, wegen eines Kommas.
Drei Fälle in drei aufeinanderfolgenden Ingests, an zwei verschiedenen Feldern (raw_files:, tags: zweimal), von drei verschiedenen Agenten. Das ist kein Ausrutscher, sondern die Fehlerrate einer Schnittstelle, die einen Wert genau einmal entgegennimmt.
Verschärfend: das Fenster ist genau ein Kommando breit.new ist laut tools/CONTRACT.md nicht idempotent, ein Retry also nicht ohne Weiteres möglich — wer im new-Aufruf danebengreift, hat den Zustand festgeschrieben, bevor er ihn prüfen konnte. Ein touch --set behebt nicht nur die drei beobachteten Fälle, es macht new von einer einmaligen Chance zu einem korrigierbaren Schritt.
Das stützt den Vorschlag aus dem vorigen Kommentar: touch --set field=value, mit derselben Coercion und Schema-Validierung wie new --set (_coerce_set_value + _validate_or_fail), Page-Ref-Arrays weiterhin ausschließlich über xref. raw rename bleibt als zweiter, unabhängiger Punkt für den Fall, dass die Datei sich bewegt.
Hinweis für die Umsetzung: mindestens ein Testfall sollte ein Arrayfeld mit Escape und mit wiederholtem --set abdecken, sonst erbt touch --set das Komma-Problem aus #12 gleich mit.
**Dritter Fall in drei Ingests, wieder `tags:`.**
Beim Ingest des dritten Transkripts (Commit `8d47e7e`) hatte ein `--set tags=`-Aufruf ein Komma am Ende; alles nach dem Trennzeichen fiel weg. `kb/concepts/Diff-Reviewable Agent Edits.md` trägt deshalb nur `[agent-workflow]` statt der vorgesehenen Liste.
Interessant ist nicht der Tippfehler, sondern dass es **keinen Weg zurück gab**. Der Agent hat die drei Auswege korrekt durchgespielt und alle drei verworfen:
- `touch` kann `tags:` nicht setzen.
- Ein Hand-Edit am Frontmatter kommt Invariante 1 zu nah.
- `rm` + `new` hätte die bereits geschriebene `concepts:`-Referenz der Source-Seite gebrochen.
Also blieb: so lassen. Der Tag ist gültig, die Seite über Titel, Summary und drei Backlinks auffindbar — aber die Kategorisierung ist dauerhaft dünner als beabsichtigt, wegen eines Kommas.
Drei Fälle in drei aufeinanderfolgenden Ingests, an zwei verschiedenen Feldern (`raw_files:`, `tags:` zweimal), von drei verschiedenen Agenten. Das ist kein Ausrutscher, sondern die Fehlerrate einer Schnittstelle, die einen Wert genau einmal entgegennimmt.
Verschärfend: **das Fenster ist genau ein Kommando breit.** `new` ist laut `tools/CONTRACT.md` nicht idempotent, ein Retry also nicht ohne Weiteres möglich — wer im `new`-Aufruf danebengreift, hat den Zustand festgeschrieben, bevor er ihn prüfen konnte. Ein `touch --set` behebt nicht nur die drei beobachteten Fälle, es macht `new` von einer einmaligen Chance zu einem korrigierbaren Schritt.
Das stützt den Vorschlag aus dem vorigen Kommentar: **`touch --set field=value`**, mit derselben Coercion und Schema-Validierung wie `new --set` (`_coerce_set_value` + `_validate_or_fail`), Page-Ref-Arrays weiterhin ausschließlich über `xref`. `raw rename` bleibt als zweiter, unabhängiger Punkt für den Fall, dass die Datei sich bewegt.
Hinweis für die Umsetzung: mindestens ein Testfall sollte ein Arrayfeld mit Escape und mit wiederholtem `--set` abdecken, sonst erbt `touch --set` das Komma-Problem aus #12 gleich mit.
--set field=value ersetzt den Wert auf der Platte. Wiederholtes --set für dasselbe Arrayfeld hängt innerhalb eines Aufrufs an, \, ist ein literales Komma — dieselben Regeln wie new --set.
--add / --remove ändern einzelne Elemente, ohne dass man die bestehende Liste kennen muss. --add ist idempotent.
--remove auf ein nicht vorhandenes Element gelingt und sagt es. Wie xref remove idempotent, aber nie stillschweigend — ein stiller No-op sieht genauso aus wie eine erfolgreiche Entfernung, und genau so verschwindet ein Tippfehler.
Gesperrt, jeweils mit Nennung des zuständigen Kommandos: type: (page-lifecycle), confidence: (abgeleitet), und die Page-Ref-Arrays (xref).
Deny statt Allow, weil eine Allowlist eine zweite Kopie des Schemas wäre — und die Kopie ist die, die driftet. Ein neu in einen Type-Spec aufgenommenes Feld bliebe stumm unbeschreibbar, bis jemand daran denkt. Invariante 8, angewandt auf eine Konstante.
Ein unbekanntes Feld wird bewusst anders abgelehnt als ein gesperrtes — nicht mit einem Verweis auf ein anderes Kommando, sondern mit der Liste dessen, was die Seite tatsächlich hat:
$ tools/wikitool touch --page "Diff-Reviewable Agent Edits" --set "tag=x"
ERROR Type types/concept.md declares no field 'tag'.
Settable fields for this page: concept_type, confidence_base, created,
modified, provenance, summary, tags
Der Anlassfall ist repariert
kb/concepts/Diff-Reviewable Agent Edits.md trug wegen eines Kommas am Ende eines --set tags=-Werts nur [agent-workflow] und war nicht mehr korrigierbar. Am echten Korpus:
$ tools/wikitool touch --page "Diff-Reviewable Agent Edits" --add "tags=context-engineering,tooling"
tags: added 'context-engineering', 'tooling'
OK Touched kb/concepts/Diff-Reviewable Agent Edits.md
$ tools/wikitool touch --page "Diff-Reviewable Agent Edits" --add "tags=context-engineering" --no-date
OK 'Diff-Reviewable Agent Edits' already up to date; nothing to change.
$ tools/wikitool touch --page "Diff-Reviewable Agent Edits" --remove "tags=vertippt" --no-date
tags: not present, nothing removed: 'vertippt'
OK 'Diff-Reviewable Agent Edits' already up to date; nothing to change.
Die Seite trägt jetzt [agent-workflow, context-engineering, tooling].
Umbau nebenbei
_coerce_set_value, _parse_set_fields und _check_raw_files_exist sind von new_page.py nach commands/_util.py gewandert. Zwei Kommandos, eine Implementierung — sonst hätte touch --set das Komma-Problem aus #12 gleich mit geerbt. Ein Test deckt genau das ab.
raw_files: bekommt beim Schreiben durch touch dieselbe Existenzprüfung wie bei new.
tests/test_touch.py ruft den Typer-Callback jetzt über einen Helfer mit Vollbelegung auf — ein direkt aufgerufener Callback bekommt für ausgelassene Argumente OptionInfo-Objekte, und ohne den Helfer kostet jede neue Option eine Änderung an sieben Aufrufstellen. Dieselbe Falle, die dist_cmd.py durch die Trennung von Wrapper und Logik vermeidet.
Prüfungen
674 Tests grün, auch mit leerem HOME und ohne globale git-Config. docs verify, instructions verify, lint sauber. Contract-Zeilen für Kommando und Fehlerkontrakt ergänzt.
Übrig
raw rename als #16 (prio/2, size/S) — der Fall, dass die Datei sich bewegt. Zweistufig ist er jetzt möglich, aber zwischen git mv und touch --set zeigt die Referenz kurz ins Leere.
Umgesetzt in **1.4.0** (`dbe2f73`).
## Die drei Entscheidungen
Torben vorgelegt und entschieden:
| Frage | Entscheidung |
|---|---|
| Welche Felder darf `--set` schreiben? | **Denylist** — alles aus dem Schema, außer einer begründeten Sperrliste |
| Was heißt `--set` für eine Liste mit Werten? | **Ersetzen, plus `--add`/`--remove`** für einzelne Elemente |
| Auch `raw rename` in diesem Durchgang? | **Nein** — eigenes Issue (#16) |
## Was `touch` jetzt kann
- **`--set field=value`** ersetzt den Wert auf der Platte. Wiederholtes `--set` für dasselbe Arrayfeld hängt *innerhalb eines Aufrufs* an, `\,` ist ein literales Komma — dieselben Regeln wie `new --set`.
- **`--add` / `--remove`** ändern einzelne Elemente, ohne dass man die bestehende Liste kennen muss. `--add` ist idempotent.
- **`--remove` auf ein nicht vorhandenes Element gelingt und sagt es.** Wie `xref remove` idempotent, aber nie stillschweigend — ein stiller No-op sieht genauso aus wie eine erfolgreiche Entfernung, und genau so verschwindet ein Tippfehler.
Gesperrt, jeweils mit Nennung des zuständigen Kommandos: `type:` (page-lifecycle), `confidence:` (abgeleitet), und die Page-Ref-Arrays (`xref`).
**Deny statt Allow, weil eine Allowlist eine zweite Kopie des Schemas wäre** — und die Kopie ist die, die driftet. Ein neu in einen Type-Spec aufgenommenes Feld bliebe stumm unbeschreibbar, bis jemand daran denkt. Invariante 8, angewandt auf eine Konstante.
Ein *unbekanntes* Feld wird bewusst anders abgelehnt als ein gesperrtes — nicht mit einem Verweis auf ein anderes Kommando, sondern mit der Liste dessen, was die Seite tatsächlich hat:
```
$ tools/wikitool touch --page "Diff-Reviewable Agent Edits" --set "tag=x"
ERROR Type types/concept.md declares no field 'tag'.
Settable fields for this page: concept_type, confidence_base, created,
modified, provenance, summary, tags
```
## Der Anlassfall ist repariert
`kb/concepts/Diff-Reviewable Agent Edits.md` trug wegen eines Kommas am Ende eines `--set tags=`-Werts nur `[agent-workflow]` und war nicht mehr korrigierbar. Am echten Korpus:
```
$ tools/wikitool touch --page "Diff-Reviewable Agent Edits" --add "tags=context-engineering,tooling"
tags: added 'context-engineering', 'tooling'
OK Touched kb/concepts/Diff-Reviewable Agent Edits.md
$ tools/wikitool touch --page "Diff-Reviewable Agent Edits" --add "tags=context-engineering" --no-date
OK 'Diff-Reviewable Agent Edits' already up to date; nothing to change.
$ tools/wikitool touch --page "Diff-Reviewable Agent Edits" --remove "tags=vertippt" --no-date
tags: not present, nothing removed: 'vertippt'
OK 'Diff-Reviewable Agent Edits' already up to date; nothing to change.
```
Die Seite trägt jetzt `[agent-workflow, context-engineering, tooling]`.
## Umbau nebenbei
`_coerce_set_value`, `_parse_set_fields` und `_check_raw_files_exist` sind von `new_page.py` nach `commands/_util.py` gewandert. Zwei Kommandos, eine Implementierung — sonst hätte `touch --set` das Komma-Problem aus #12 gleich mit geerbt. Ein Test deckt genau das ab.
`raw_files:` bekommt beim Schreiben durch `touch` dieselbe Existenzprüfung wie bei `new`.
`tests/test_touch.py` ruft den Typer-Callback jetzt über einen Helfer mit Vollbelegung auf — ein direkt aufgerufener Callback bekommt für ausgelassene Argumente `OptionInfo`-Objekte, und ohne den Helfer kostet jede neue Option eine Änderung an sieben Aufrufstellen. Dieselbe Falle, die `dist_cmd.py` durch die Trennung von Wrapper und Logik vermeidet.
## Prüfungen
674 Tests grün, auch mit leerem `HOME` und ohne globale git-Config. `docs verify`, `instructions verify`, `lint` sauber. Contract-Zeilen für Kommando und Fehlerkontrakt ergänzt.
## Übrig
`raw rename` als **#16** (`prio/2`, `size/S`) — der Fall, dass die Datei sich bewegt. Zweistufig ist er jetzt möglich, aber zwischen `git mv` und `touch --set` zeigt die Referenz kurz ins Leere.
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.
Der Anlass
Beim Zurückbenennen der Rohdatei aus #12 (
5426a6e) mussteraw_files:der Source-Seite von Hand korrigiert werden. Es gibt keinwikitool-Kommando dafür.touchschreibt die Felder, die die Seite selbst beschreiben (modified:,summary:,provenance:,confidence_base:) —raw_files:gehört nicht dazu.xref add/remove/link-sourcefassen die Page-Ref-Arrays an (related:,sources:,entities:,concepts:).raw_files:ist keins davon: es zeigt auf einen Pfad, nicht auf einen Seitentitel.new source --set raw_files=…schreibt das Feld einmal, beim Anlegen. Danach nie wieder.Invariante 1 verbietet das Hand-Editieren hier nicht — ihre Liste nennt Katalog,
log.md,provenance.md, die Skill-Verzeichnisse, die beiden JSON-Dateien und die Page-Ref-Arrays, undraw_files:steht in keiner davon. Trotzdem widerspricht es dem Kernprinzip: „anything mechanical is done bytools/wikitool, never by hand". Eine Pfadkorrektur in einem Frontmatter-Array ist genau das.Wann das auftritt
Nicht nur beim Reparieren eines Bugs. Jedes Mal, wenn eine Rohdatei ihren Pfad ändert:
raw/CONTRACT.md(notes/→documents/, wenn sich herausstellt, dass es ein Dokument war)raw/-Struktur nachträglich umbaut (sieheinstructions/ingest-large-tree.md)lintundsources coveragemelden den gebrochenen Zustand zuverlässig (Broken raw_files references), aber nichts kann ihn beheben. Das ist die Asymmetrie, die dieses Issue schließen soll: eine Prüfung ohne zugehörige Reparatur schickt den Agenten in genau die Hand-Edits, gegen die der Rest des Stacks gebaut ist.Vorschlag für den Schnitt
wikitool sources relink --page "<Titel>" --from <alter-pfad> --to <neuer-pfad>— und/oder ein additives--add/--remove.Offene Fragen für die Umsetzung:
page-lifecycle.mdgenau diese Frage überwikitool rename, das beides tut. Analog wäre einraw rename <alt> <neu>, dasgit mvund jede referenzierende Source-Seite anfasst, die geschlossenere Form — und die einzige, bei der der Zwischenzustand „Datei weg, Referenz zeigt ins Leere" nie entsteht.raw/liegt (_check_raw_files_existkann das schon).lintführt das als eigenen Befund (duplicate_raw_file_owners), das Kommando sollte es nicht stillschweigend erzeugen.sources rebuild-indexdanach automatisch, oder dem Aufrufer überlassen? Die anderen Kommandos überlassen es ihm.Nicht Teil davon
Rohdateien mit einem
raw-Unterbefehl generell zu verwalten.raw/ist unveränderlich; das hier ist der eng begrenzte Fall, dass ein Pfad sich ändert, während der Inhalt derselbe bleibt.Akzeptanzkriterien
raw_files:einer bestehenden Seite, ohne dass jemand Frontmatter anfasst.5426a6e— Rohdatei umbenannt, eine Source-Seite referenziert sie — ist der Test.tools/CONTRACT.mdfür Kommando und Fehlerkontrakt;docs verifyerzwingt beide Richtungen.instructions/page-lifecycle.mderwähnt den Rohdatei-Fall, oder sagt ausdrücklich, dass er woanders steht. Heute schweigt es dazu, und sein Scope-Abschnitt grenzt nur gegen Contracts/Instructions/Type-Specs ab.Hinweis
Der Anlassfall selbst ist erledigt und braucht keine Nacharbeit:
sources coveragemeldet 0 unabgedeckt und 0 gebrochene Referenzen,lintist sauber über 255 Seiten. Dieses Issue ist die Lücke im Werkzeug, nicht der Schaden.Zweiter Fall derselben Lücke, anderes Feld:
tags:.Beim Ingest des Transkripts zu diesem Fix (Commit
8764799) musstetags:auf zwei neu angelegten Seiten von Hand gesetzt werden.newnimmt das Feld nur bei der Erstellung entgegen; wird es dort vergessen, gibt es keinen Weg zurück —touchschreibt nur die Felder, die die Seite selbst beschreiben (modified:,summary:,provenance:,confidence_base:), undtags:gehört nicht dazu.Wie
raw_files:fällttags:nicht unter Invariante 1 — die Liste dort nennt Katalog,log.md,provenance.md, die Skill-Verzeichnisse, die beiden JSON-Dateien und die Page-Ref-Arrays. Und wie beiraw_files:ist die Handeditierung trotzdem der einzige Weg.Das verschiebt den Zuschnitt dieses Issues. Es geht nicht um ein Kommando für
raw_files:, sondern um eine Klasse: Frontmatter-Felder, die beinewschreibbar sind und danach nie wieder. Einsources relinkwürderaw_files:lösen undtags:offen lassen, und beim nächsten Feld stünde derselbe Kommentar hier.Zwei Zuschnitte, die beide mehr treffen als ein Einzelkommando:
touch --set field=valuemit derselben Coercion und Schema-Validierung, dienew --setschon hat (_coerce_set_value+_validate_or_fail). Deckttags:,raw_files:und jedes künftige skalare oder Array-Feld ab, das kein Page-Ref-Array ist. Die Page-Ref-Arrays blieben ausdrücklich beixref, weil dort die Gegenrichtung mitgepflegt wird.raw rename <alt> <neu>zusätzlich, für den Fall, dass die Datei sich bewegt —git mvplus jede referenzierende Source-Seite in einem Schritt, damit der Zwischenzustand „Datei weg, Referenz zeigt ins Leere" nie entsteht. Das ist der Teil, den (1) nicht abdeckt.Vorschlag: Titel und Scope auf (1) erweitern, (2) als zweiten Akzeptanzpunkt behalten.
Verwandt: der Ingest hat das Muster als Concept-Seite
Detect-Repair Asymmetryabgelegt — ein Check meldet einen Defekt zuverlässig (lint,sources coverage), kein Befehl behebt ihn, also bleibt die Handeditierung. Das ist die Verallgemeinerung dieses Issues.Dritter Fall in drei Ingests, wieder
tags:.Beim Ingest des dritten Transkripts (Commit
8d47e7e) hatte ein--set tags=-Aufruf ein Komma am Ende; alles nach dem Trennzeichen fiel weg.kb/concepts/Diff-Reviewable Agent Edits.mdträgt deshalb nur[agent-workflow]statt der vorgesehenen Liste.Interessant ist nicht der Tippfehler, sondern dass es keinen Weg zurück gab. Der Agent hat die drei Auswege korrekt durchgespielt und alle drei verworfen:
touchkanntags:nicht setzen.rm+newhätte die bereits geschriebeneconcepts:-Referenz der Source-Seite gebrochen.Also blieb: so lassen. Der Tag ist gültig, die Seite über Titel, Summary und drei Backlinks auffindbar — aber die Kategorisierung ist dauerhaft dünner als beabsichtigt, wegen eines Kommas.
Drei Fälle in drei aufeinanderfolgenden Ingests, an zwei verschiedenen Feldern (
raw_files:,tags:zweimal), von drei verschiedenen Agenten. Das ist kein Ausrutscher, sondern die Fehlerrate einer Schnittstelle, die einen Wert genau einmal entgegennimmt.Verschärfend: das Fenster ist genau ein Kommando breit.
newist lauttools/CONTRACT.mdnicht idempotent, ein Retry also nicht ohne Weiteres möglich — wer imnew-Aufruf danebengreift, hat den Zustand festgeschrieben, bevor er ihn prüfen konnte. Eintouch --setbehebt nicht nur die drei beobachteten Fälle, es machtnewvon einer einmaligen Chance zu einem korrigierbaren Schritt.Das stützt den Vorschlag aus dem vorigen Kommentar:
touch --set field=value, mit derselben Coercion und Schema-Validierung wienew --set(_coerce_set_value+_validate_or_fail), Page-Ref-Arrays weiterhin ausschließlich überxref.raw renamebleibt als zweiter, unabhängiger Punkt für den Fall, dass die Datei sich bewegt.Hinweis für die Umsetzung: mindestens ein Testfall sollte ein Arrayfeld mit Escape und mit wiederholtem
--setabdecken, sonst erbttouch --setdas Komma-Problem aus #12 gleich mit.Umgesetzt in 1.4.0 (
dbe2f73).Die drei Entscheidungen
Torben vorgelegt und entschieden:
--setschreiben?--setfür eine Liste mit Werten?--add/--removefür einzelne Elementeraw renamein diesem Durchgang?Was
touchjetzt kann--set field=valueersetzt den Wert auf der Platte. Wiederholtes--setfür dasselbe Arrayfeld hängt innerhalb eines Aufrufs an,\,ist ein literales Komma — dieselben Regeln wienew --set.--add/--removeändern einzelne Elemente, ohne dass man die bestehende Liste kennen muss.--addist idempotent.--removeauf ein nicht vorhandenes Element gelingt und sagt es. Wiexref removeidempotent, aber nie stillschweigend — ein stiller No-op sieht genauso aus wie eine erfolgreiche Entfernung, und genau so verschwindet ein Tippfehler.Gesperrt, jeweils mit Nennung des zuständigen Kommandos:
type:(page-lifecycle),confidence:(abgeleitet), und die Page-Ref-Arrays (xref).Deny statt Allow, weil eine Allowlist eine zweite Kopie des Schemas wäre — und die Kopie ist die, die driftet. Ein neu in einen Type-Spec aufgenommenes Feld bliebe stumm unbeschreibbar, bis jemand daran denkt. Invariante 8, angewandt auf eine Konstante.
Ein unbekanntes Feld wird bewusst anders abgelehnt als ein gesperrtes — nicht mit einem Verweis auf ein anderes Kommando, sondern mit der Liste dessen, was die Seite tatsächlich hat:
Der Anlassfall ist repariert
kb/concepts/Diff-Reviewable Agent Edits.mdtrug wegen eines Kommas am Ende eines--set tags=-Werts nur[agent-workflow]und war nicht mehr korrigierbar. Am echten Korpus:Die Seite trägt jetzt
[agent-workflow, context-engineering, tooling].Umbau nebenbei
_coerce_set_value,_parse_set_fieldsund_check_raw_files_existsind vonnew_page.pynachcommands/_util.pygewandert. Zwei Kommandos, eine Implementierung — sonst hättetouch --setdas Komma-Problem aus #12 gleich mit geerbt. Ein Test deckt genau das ab.raw_files:bekommt beim Schreiben durchtouchdieselbe Existenzprüfung wie beinew.tests/test_touch.pyruft den Typer-Callback jetzt über einen Helfer mit Vollbelegung auf — ein direkt aufgerufener Callback bekommt für ausgelassene ArgumenteOptionInfo-Objekte, und ohne den Helfer kostet jede neue Option eine Änderung an sieben Aufrufstellen. Dieselbe Falle, diedist_cmd.pydurch die Trennung von Wrapper und Logik vermeidet.Prüfungen
674 Tests grün, auch mit leerem
HOMEund ohne globale git-Config.docs verify,instructions verify,lintsauber. Contract-Zeilen für Kommando und Fehlerkontrakt ergänzt.Übrig
raw renameals #16 (prio/2,size/S) — der Fall, dass die Datei sich bewegt. Zweistufig ist er jetzt möglich, aber zwischengit mvundtouch --setzeigt die Referenz kurz ins Leere.