publish --path pusht nach einem mechanischen Reconcile einen veralteten Katalog und ein Log ohne die eigenen Einträge #182

Closed
opened 2026-10-06 08:28:51 +00:00 by torben · 3 comments
Owner

Abgetrennt aus #180, dort unter "Bewusst nicht behandelt" festgehalten. Am 2026-10-06 gegen fa106ee (8.0.0-beta.47) nachgestellt.

Ergebnis

Behoben in 8.0.0-beta.48, Commit a3fc8f6 (2026-10-06), nach Entscheidung 2 (Option A). Verifiziert: volle Suite auf leerer Maschine (2328 passed, 3 skipped), docs verify, instructions verify, CI grün auf a3fc8f6 - Läufe 563 und 564. Abschlussprüfung: kein docs/-Text, kein README und keine Instruktion beschrieb das alte --path-Verhalten so, dass es nachzuziehen war (instructions/gates.md, docs/why-gates-are-code.md, README.md, tools/README.md gelesen); der publish-Record wurde im Build nachgezogen.

Problem (bis 8.0.0-beta.47)

Seit 8.0.0-beta.46 führt reconcile Überschneidungen in generierten Dateien (kb/index.md, kb/log.md, kb/provenance.md, kb/**/INDEX.md) mechanisch zusammen und erzeugt Katalog und Provenance danach neu; die Ergebnisse - samt der lokal angehängten Log-Einträge - liegen als ungespeicherte Änderung im Arbeitsbaum. Ein normaler publish nahm sie mit, weil sein proaktiver Reconcile vor dem Staging läuft.

Ein publish --path <X> stagte und committete aber nur <X> (collect_changes(paths), git add -A -- <paths>, git commit -- <paths>). Lagen die generierten Dateien außerhalb von <X>, galt nach dem Push:

  • die neue Seite war im gepushten Stand,
  • der gepushte Katalog war der der eingehenden Seite - er kannte die Seite nicht,
  • dem gepushten kb/log.md fehlten die lokalen Einträge,
  • lokal lagen die neu erzeugten Dateien ungespeichert, bis ein publish ohne --path sie mitnahm.

Nachgestellt (temporärer Test auf der Fixture wiki_repo aus tools/chemenu/tests/test_git_publish.py: Writer pusht ein Ingest, lokal ein ungespeichertes Ingest, dann publish --path kb/concepts/protocols): Seite gepusht True, [[Local]] im gepushten kb/concepts/INDEX.md False, gepushtes Log ['Base', 'Writer'], lokal M kb/concepts/INDEX.md u. a.

Verloren ging nichts - die Einträge lagen lokal -, aber der Remote war bis zum nächsten vollen publish inkonsistent, und eine andere Sitzung, die in der Zwischenzeit syncte, arbeitete gegen diesen Stand.

Entscheidungen

  1. Die generierten Pfade kommen automatisch in den Umfang (Betreiber, 2026-10-06; Option 1 von drei - verworfen wurden "vor dem Staging mit Exit 1 abbrechen", weil es einen Batch-Lauf unterbricht, obwohl nichts Riskantes passiert, und "nur warnen", weil der inkonsistente Remote bliebe). Begründung: Die Dateien tragen keine Entscheidung, und der Mass-Update-Gate hat für dieselben Dateien dieselbe Abwägung schon getroffen (committet, aber nicht gezählt). --path schneidet Entscheidungen in prüfbare Portionen; daran ändert sich nichts. Still ist es nicht: die Ausgabe nennt die hinzugenommenen Pfade. Damit ist der Satz aus #180, den Umfang still zu erweitern hätte --path unterlaufen, für genau diese Dateien entschieden.
  2. Unabhängig davon, ob der Reconcile dieses Laufs etwas neu erzeugt hat (Betreiber, 2026-10-06, Option A; im Build gefunden). Der erste Entwurf band das Mitnehmen an outcome.regenerated. Das überlebt eine Gate-Ablehnung nicht: der mit Exit 42 abgelehnte Lauf hat den Reconcile schon gemacht, der freigegebene Re-run mit --confirm findet up-to-date, regenerated ist False, und er pushte wieder den inkonsistenten Stand (Test: gepushtes Log ['Base', 'Writer']). Ebenso sync, dann publish --path. Am Baum ist das nicht vom Fall ohne Reconcile zu unterscheiden. Verworfen: B - die Neuerzeugung als Marke unter .git/ vermerken (neuer Zustand, der verwaisen kann); C - die Lücke dokumentieren (der Gate-Weg bliebe offen). Die Begründung von Entscheidung 1 hängt nicht daran, wer die Dateien geschrieben hat. Folge: das ursprüngliche Kriterium 2 ("ohne Neuerzeugung nur <X>") kehrte sich für generierte Dateien um; alle anderen Dateien außerhalb von <X> bleiben, wie sie sind.

Bekannte Folge, hingenommen: Liegen ungespeicherte Seiten auch außerhalb von <X>, nennt der mitgenommene Katalog sie schon, bevor sie gepusht sind - bis der nächste publish sie nachzieht. Vorher war es umgekehrt (Seite gepusht, Katalog kannte sie nicht). Kriterium 1 setzt voraus, dass alle ungespeicherten Seiten unter <X> liegen.

Umsetzung

In publish_command, nach dem proaktiven Reconcile (bzw. ohne ihn bei --no-push) und vor collect_changes:

  • Wenn paths nicht leer ist: generated_outside(paths) bestimmt die ungespeicherten generierten Pfade, die git add -A -- <paths> liegen ließe - Git entscheidet per Pathspec, was unter paths liegt (_dirty_paths() nimmt dafür eine optionale Pathspec). Sie kommen an einen Staging-Umfang staging; eine Zeile nennt sie: Including N generated file(s) outside --path: ....
  • staging ersetzt paths nur dort, wo gestaged und committet wird: collect_changes, git add -A --, git commit --. Token und Re-run-Zeile behalten das ursprüngliche paths; counted_files_of lässt die generierten Dateien ohnehin heraus.
  • Ohne --path läuft alles wie bisher. Der Retry-Pfad nach abgelehntem Push brauchte nichts: commit_regenerated committet die generierten Dateien dort schon unabhängig von --path.
  • Record von publish: die --path-Note nennt die Ausnahme, die Reconcile-Note verweist darauf. instructions/gates.md (--path als Batch-Weg) blieb wahr und unverändert.

Akzeptanzkriterien

  • Nach einem publish --path <X>, dessen Reconcile generierte Dateien neu erzeugt hat - direkt, nach einer Gate-Ablehnung mit --confirm, oder nach einem sync -, gleichen Katalog und kb/provenance.md am gepushten Tip Byte für Byte dem, was index rebuild/sources rebuild-index am selben Tag darauf erzeugen, und jeder lokale Log-Eintrag steht genau einmal im gepushten kb/log.md; lokal bleibt danach keine generierte Datei ungespeichert.
  • Ohne jeden Reconcile gilt dasselbe für den Katalog und das Log, die das eigene Ingest geschrieben hat; jede nicht generierte Datei außerhalb von <X> bleibt ungespeichert und uncommittet (ersetzt das ursprüngliche Kriterium 2, Entscheidung 2).
  • Die Ausgabe nennt die hinzugenommenen generierten Pfade; liegen sie schon unter <X>, wird nichts hinzugenommen und nichts genannt.
  • Der Mass-Update-Gate zählt sie weiterhin nicht, und der --confirm-Token sowie die Re-run-Zeile eines publish --path <X> sind mit und ohne die hinzugenommenen Pfade identisch.
  • pytest, docs verify, instructions verify grün; CI grün (Läufe 563 und 564 auf a3fc8f6).

Betroffene Stellen

  • tools/chemenu/commands/git_publish.py: publish_command, generated_outside, _dirty_paths(pathspec), Record von publish
  • tools/CONTRACT.md per docs contract --apply; CHANGES.md (Changeset unter 8.0.0-beta.48)
  • Tests: tools/chemenu/tests/test_git_publish.py, Abschnitt "publish --path and the uncommitted generated files outside it" (sechs Tests, Fixture wiki_repo)

Priorität

War prio/waiting mit dem Auslöser "publish --path auf einer Instanz, die von mehr als einem Checkout oder einer parallelen Sitzung aus beschrieben wird"; vom Betreiber am 2026-10-06 ohne eingetretenen Auslöser zum Bau aufgerufen.

Versionsteil

--patch, Drop-in: kein Befehl, Flag, Dateiformat oder Zustandsfile ändert sich; ein --confirm-Token bleibt gültig. Sichtbar anders ist nur, dass ein publish --path die generierten Dateien mitcommittet, statt sie liegen zu lassen. Der Bump-Titel in CHANGES.md ist deutsch geblieben (von version bump geschrieben, kein Werkzeug zum Umbenennen); der Changeset-Text ist englisch.

Abgetrennt aus #180, dort unter "Bewusst nicht behandelt" festgehalten. Am 2026-10-06 gegen `fa106ee` (`8.0.0-beta.47`) nachgestellt. ## Ergebnis Behoben in **`8.0.0-beta.48`**, Commit **`a3fc8f6`** (2026-10-06), nach Entscheidung 2 (Option A). Verifiziert: volle Suite auf leerer Maschine (2328 passed, 3 skipped), `docs verify`, `instructions verify`, **CI grün auf `a3fc8f6` - Läufe [563](https://gitea.nehmer.net/torben/chemenu/actions/runs/563) und [564](https://gitea.nehmer.net/torben/chemenu/actions/runs/564)**. Abschlussprüfung: kein `docs/`-Text, kein README und keine Instruktion beschrieb das alte `--path`-Verhalten so, dass es nachzuziehen war (`instructions/gates.md`, `docs/why-gates-are-code.md`, `README.md`, `tools/README.md` gelesen); der `publish`-Record wurde im Build nachgezogen. ## Problem (bis `8.0.0-beta.47`) Seit `8.0.0-beta.46` führt `reconcile` Überschneidungen in generierten Dateien (`kb/index.md`, `kb/log.md`, `kb/provenance.md`, `kb/**/INDEX.md`) mechanisch zusammen und erzeugt Katalog und Provenance danach neu; die Ergebnisse - samt der lokal angehängten Log-Einträge - liegen als ungespeicherte Änderung im Arbeitsbaum. Ein normaler `publish` nahm sie mit, weil sein proaktiver Reconcile vor dem Staging läuft. Ein `publish --path <X>` stagte und committete aber nur `<X>` (`collect_changes(paths)`, `git add -A -- <paths>`, `git commit -- <paths>`). Lagen die generierten Dateien außerhalb von `<X>`, galt nach dem Push: - die neue Seite war im gepushten Stand, - der gepushte Katalog war der der eingehenden Seite - er kannte die Seite nicht, - dem gepushten `kb/log.md` fehlten die lokalen Einträge, - lokal lagen die neu erzeugten Dateien ungespeichert, bis ein `publish` ohne `--path` sie mitnahm. **Nachgestellt** (temporärer Test auf der Fixture `wiki_repo` aus `tools/chemenu/tests/test_git_publish.py`: Writer pusht ein Ingest, lokal ein ungespeichertes Ingest, dann `publish --path kb/concepts/protocols`): Seite gepusht `True`, `[[Local]]` im gepushten `kb/concepts/INDEX.md` `False`, gepushtes Log `['Base', 'Writer']`, lokal ` M kb/concepts/INDEX.md` u. a. Verloren ging nichts - die Einträge lagen lokal -, aber der Remote war bis zum nächsten vollen `publish` inkonsistent, und eine andere Sitzung, die in der Zwischenzeit `sync`te, arbeitete gegen diesen Stand. ## Entscheidungen 1. **Die generierten Pfade kommen automatisch in den Umfang** (Betreiber, 2026-10-06; Option 1 von drei - verworfen wurden "vor dem Staging mit Exit 1 abbrechen", weil es einen Batch-Lauf unterbricht, obwohl nichts Riskantes passiert, und "nur warnen", weil der inkonsistente Remote bliebe). Begründung: Die Dateien tragen keine Entscheidung, und der Mass-Update-Gate hat für dieselben Dateien dieselbe Abwägung schon getroffen (committet, aber nicht gezählt). `--path` schneidet Entscheidungen in prüfbare Portionen; daran ändert sich nichts. Still ist es nicht: die Ausgabe nennt die hinzugenommenen Pfade. Damit ist der Satz aus #180, den Umfang still zu erweitern hätte `--path` unterlaufen, für genau diese Dateien entschieden. 2. **Unabhängig davon, ob der Reconcile dieses Laufs etwas neu erzeugt hat** (Betreiber, 2026-10-06, Option A; im Build gefunden). Der erste Entwurf band das Mitnehmen an `outcome.regenerated`. Das überlebt eine Gate-Ablehnung nicht: der mit Exit 42 abgelehnte Lauf hat den Reconcile schon gemacht, der freigegebene Re-run mit `--confirm` findet `up-to-date`, `regenerated` ist `False`, und er pushte wieder den inkonsistenten Stand (Test: gepushtes Log `['Base', 'Writer']`). Ebenso `sync`, dann `publish --path`. Am Baum ist das nicht vom Fall ohne Reconcile zu unterscheiden. Verworfen: **B** - die Neuerzeugung als Marke unter `.git/` vermerken (neuer Zustand, der verwaisen kann); **C** - die Lücke dokumentieren (der Gate-Weg bliebe offen). Die Begründung von Entscheidung 1 hängt nicht daran, wer die Dateien geschrieben hat. Folge: das ursprüngliche Kriterium 2 ("ohne Neuerzeugung nur `<X>`") kehrte sich für generierte Dateien um; alle anderen Dateien außerhalb von `<X>` bleiben, wie sie sind. **Bekannte Folge, hingenommen:** Liegen ungespeicherte Seiten auch außerhalb von `<X>`, nennt der mitgenommene Katalog sie schon, bevor sie gepusht sind - bis der nächste `publish` sie nachzieht. Vorher war es umgekehrt (Seite gepusht, Katalog kannte sie nicht). Kriterium 1 setzt voraus, dass alle ungespeicherten Seiten unter `<X>` liegen. ## Umsetzung In `publish_command`, nach dem proaktiven Reconcile (bzw. ohne ihn bei `--no-push`) und vor `collect_changes`: - Wenn `paths` nicht leer ist: `generated_outside(paths)` bestimmt die ungespeicherten generierten Pfade, die `git add -A -- <paths>` liegen ließe - Git entscheidet per Pathspec, was unter `paths` liegt (`_dirty_paths()` nimmt dafür eine optionale Pathspec). Sie kommen an einen **Staging-Umfang** `staging`; eine Zeile nennt sie: `Including N generated file(s) outside --path: ...`. - `staging` ersetzt `paths` nur dort, wo gestaged und committet wird: `collect_changes`, `git add -A --`, `git commit --`. **Token und Re-run-Zeile behalten das ursprüngliche `paths`**; `counted_files_of` lässt die generierten Dateien ohnehin heraus. - Ohne `--path` läuft alles wie bisher. Der Retry-Pfad nach abgelehntem Push brauchte nichts: `commit_regenerated` committet die generierten Dateien dort schon unabhängig von `--path`. - Record von `publish`: die `--path`-Note nennt die Ausnahme, die Reconcile-Note verweist darauf. `instructions/gates.md` (`--path` als Batch-Weg) blieb wahr und unverändert. ## Akzeptanzkriterien - [x] Nach einem `publish --path <X>`, dessen Reconcile generierte Dateien neu erzeugt hat - direkt, nach einer Gate-Ablehnung mit `--confirm`, oder nach einem `sync` -, gleichen Katalog und `kb/provenance.md` am gepushten Tip Byte für Byte dem, was `index rebuild`/`sources rebuild-index` am selben Tag darauf erzeugen, und jeder lokale Log-Eintrag steht genau einmal im gepushten `kb/log.md`; lokal bleibt danach keine generierte Datei ungespeichert. - [x] Ohne jeden Reconcile gilt dasselbe für den Katalog und das Log, die das eigene Ingest geschrieben hat; jede **nicht** generierte Datei außerhalb von `<X>` bleibt ungespeichert und uncommittet (ersetzt das ursprüngliche Kriterium 2, Entscheidung 2). - [x] Die Ausgabe nennt die hinzugenommenen generierten Pfade; liegen sie schon unter `<X>`, wird nichts hinzugenommen und nichts genannt. - [x] Der Mass-Update-Gate zählt sie weiterhin nicht, und der `--confirm`-Token sowie die Re-run-Zeile eines `publish --path <X>` sind mit und ohne die hinzugenommenen Pfade identisch. - [x] `pytest`, `docs verify`, `instructions verify` grün; CI grün (Läufe 563 und 564 auf `a3fc8f6`). ## Betroffene Stellen - `tools/chemenu/commands/git_publish.py`: `publish_command`, `generated_outside`, `_dirty_paths(pathspec)`, Record von `publish` - `tools/CONTRACT.md` per `docs contract --apply`; `CHANGES.md` (Changeset unter `8.0.0-beta.48`) - Tests: `tools/chemenu/tests/test_git_publish.py`, Abschnitt "publish --path and the uncommitted generated files outside it" (sechs Tests, Fixture `wiki_repo`) ## Priorität War `prio/waiting` mit dem Auslöser "`publish --path` auf einer Instanz, die von mehr als einem Checkout oder einer parallelen Sitzung aus beschrieben wird"; vom Betreiber am 2026-10-06 ohne eingetretenen Auslöser zum Bau aufgerufen. ## Versionsteil **`--patch`**, Drop-in: kein Befehl, Flag, Dateiformat oder Zustandsfile ändert sich; ein `--confirm`-Token bleibt gültig. Sichtbar anders ist nur, dass ein `publish --path` die generierten Dateien mitcommittet, statt sie liegen zu lassen. Der Bump-Titel in `CHANGES.md` ist deutsch geblieben (von `version bump` geschrieben, kein Werkzeug zum Umbenennen); der Changeset-Text ist englisch.
torben added the prio/waitingsize/Sarea/workflowkind/decision labels 2026-10-06 08:28:51 +00:00
torben added kind/build and removed kind/decision labels 2026-10-06 08:30:06 +00:00
Author
Owner

Changelog: Offene Frage entschieden (Betreiber): Option 1, jetzt Entscheidung 1 mit den verworfenen Alternativen. Neu: Abschnitt "Entwurf" - Staging-Umfang nur bei --path und regenerated; Token und Re-run-Zeile behalten das ursprüngliche --path, weil changeset_token es einschließt. Akzeptanzkriterien präzisiert (nichts bleibt lokal ungespeichert; keine Hinzunahme, wenn schon unter --path; Token und Re-run-Zeile identisch), Versionsteil festgelegt (--patch). Neu gelabelt: kind/decision → kind/build; prio/waiting bleibt mit seinem Auslöser.

**Changelog:** Offene Frage entschieden (Betreiber): Option 1, jetzt Entscheidung 1 mit den verworfenen Alternativen. Neu: Abschnitt "Entwurf" - Staging-Umfang nur bei `--path` und `regenerated`; Token und Re-run-Zeile behalten das ursprüngliche `--path`, weil `changeset_token` es einschließt. Akzeptanzkriterien präzisiert (nichts bleibt lokal ungespeichert; keine Hinzunahme, wenn schon unter `--path`; Token und Re-run-Zeile identisch), Versionsteil festgelegt (`--patch`). Neu gelabelt: `kind/decision` → `kind/build`; `prio/waiting` bleibt mit seinem Auslöser.
Author
Owner

Changelog: Gebaut und publiziert (8.0.0-beta.48, a3fc8f6, CI-Läufe 563/564 grün). Neu: Entscheidung 2 (Option A) - die Bedingung outcome.regenerated ist gefallen, weil sie eine Gate-Ablehnung nicht überlebt (der --confirm-Re-run findet up-to-date) und sync-dann-publish --path nicht deckt; verworfen B (Marke unter .git/) und C (dokumentieren). Kriterium 2 ersetzt: auch ohne Reconcile kommen generierte Dateien mit, andere Dateien außerhalb von <X> nicht. Festgehalten: der mitgenommene Katalog kann ungespeicherte Seiten außerhalb von <X> vorab nennen. "Entwurf" ist jetzt "Umsetzung"; alle Kriterien abgehakt.

**Changelog:** Gebaut und publiziert (`8.0.0-beta.48`, `a3fc8f6`, CI-Läufe 563/564 grün). Neu: Entscheidung 2 (Option A) - die Bedingung `outcome.regenerated` ist gefallen, weil sie eine Gate-Ablehnung nicht überlebt (der `--confirm`-Re-run findet `up-to-date`) und `sync`-dann-`publish --path` nicht deckt; verworfen B (Marke unter `.git/`) und C (dokumentieren). Kriterium 2 ersetzt: auch ohne Reconcile kommen generierte Dateien mit, andere Dateien außerhalb von `<X>` nicht. Festgehalten: der mitgenommene Katalog kann ungespeicherte Seiten außerhalb von `<X>` vorab nennen. "Entwurf" ist jetzt "Umsetzung"; alle Kriterien abgehakt.
Author
Owner

Changelog: Body in Endfassung: "Stand" ist jetzt "Ergebnis" (Version, Commit, verifizierende Läufe 563/564, Abschlussprüfung der Doku ohne Befund), Problem und Priorität in der Vergangenheit. Geschlossen.

**Changelog:** Body in Endfassung: "Stand" ist jetzt "Ergebnis" (Version, Commit, verifizierende Läufe 563/564, Abschlussprüfung der Doku ohne Befund), Problem und Priorität in der Vergangenheit. Geschlossen.
Sign in to join this conversation.
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: torben/chemenu#182