From b2f7dec122ce6c6e04a3757f38fa3141bf4e85af Mon Sep 17 00:00:00 2001 From: Torben Nehmer Date: Tue, 1 Sep 2026 19:03:23 +0200 Subject: [PATCH] fix: private-instance - Demo-Korpus wandert beim Merge mit, Prozedur korrigiert (2.2.1) Files changed: - CHANGES.md - VERSION - instructions/private-instance.md --- CHANGES.md | 44 +++++++++++++++++++++++ VERSION | 2 +- instructions/private-instance.md | 60 ++++++++++++++++++++++++++++---- 3 files changed, 98 insertions(+), 8 deletions(-) diff --git a/CHANGES.md b/CHANGES.md index 4593bb3..3d2279f 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -20,6 +20,50 @@ their date-only headings. --- +## 2.2.1 - 2026-09-01 - private-instance: der Demo-Korpus wandert beim Merge doch mit - Prozedur korrigiert + +**Author:** Torben Nehmer + +`instructions/private-instance.md` behauptete in 2.2.0, ein `git merge upstream/main` löse +Änderungen am Demo-Korpus stillschweigend auf, weil die private Instanz ihn einmal gelöscht hat: +*deleted-in-ours, unmodified-in-theirs*. Das war **nicht gemessen, sondern angenommen** — und es +ist falsch. Ein Nachbau mit einem Upstream, der seinen Korpus bewegt, zeigt drei verschiedene +Verhalten: + +| Upstream tut | `git merge upstream/main` tut | +|---|---| +| ändert eine Seite, die du gelöscht hast | `CONFLICT (modify/delete)` — und **lässt die Upstream-Fassung im Arbeitsbaum liegen**. Ein `git add -A` beim Auflösen holt die Demo-Seite zurück. | +| legt eine neue Seite an | staged sie **stillschweigend**. Kein Konflikt, keine Meldung. | +| löscht eine Seite, die du auch gelöscht hast | nichts. Der einzige harmlose Fall. | + +Die mittlere Zeile ist die gefährliche, weil nichts sie ankündigt. Ein Upstream, der einen +Demo-Korpus ausliefert **und** ihn als Testbett benutzt, legt Seiten an — und jede einzelne +landet in der privaten Instanz und taucht dort in `lint`, `index`, `search` und +`confidence decay` auf. Genau diese Doppelnutzung beschreibt Issue #28. + +**Korrigiert:** Die Update-Prozedur hält den Merge mit `--no-commit` offen, zwingt `kb/` und +`raw/` danach auf den eigenen Stand zurück (`git rm --cached`, `rm -rf`, `git checkout HEAD --`) +und schließt ihn erst dann. Solange der Merge offen ist, zeigt `HEAD` noch auf den Commit davor, +und genau das macht den Schritt sauber. Anschließend eine Kontrolle, die man nicht überlesen +kann: + +```bash +git diff --name-only $BEFORE HEAD -- kb raw # muss leer sein +``` + +Das Rezept ist wörtlich so nachgespielt worden, wie es jetzt in der Datei steht — mit einem +Upstream, der gleichzeitig eine Seite ändert, eine anlegt, eine löscht und dasselbe unter +`raw/` tut. Ergebnis: Stack aktualisiert, nur eigener Inhalt übrig, Kontrolle leer, +Arbeitsbaum sauber. + +**Auch die Decision Points korrigiert.** „Konflikt in `kb/` per Hand als *keep deleted* +auflösen" stand vorher da und ist der Rat, der in die Falle führt: `git add -A` committet die +Fassung, die git im Arbeitsbaum liegen gelassen hat. + +**Dateien:** `instructions/private-instance.md`. + +--- + ## 2.2.0 - 2026-09-01 - Publish-Remote Gate: publish schreibt nur an erklaerte Ziele **Author:** Torben Nehmer diff --git a/VERSION b/VERSION index ccbccc3..c043eea 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -2.2.0 +2.2.1 diff --git a/instructions/private-instance.md b/instructions/private-instance.md index 50bc25b..bdf68e5 100644 --- a/instructions/private-instance.md +++ b/instructions/private-instance.md @@ -25,9 +25,24 @@ merge, so it cannot notice that the receiving instance changed a file, and it ha surface, so nobody learns when upstream and local both touched the same one. It overwrites silently. -A clone gets all of that from git. The private `main` deletes the upstream's demo corpus once; -every later `git merge upstream/main` sees *deleted-in-ours, unmodified-in-theirs* and resolves -without asking. Stack changes land as real merges, with real conflicts where they conflict. +A clone gets all of that from git. Stack changes land as real merges, with real conflicts where +they conflict. + +**What a plain `git merge` does *not* give you is protection from the upstream's content.** The +private `main` deletes the demo corpus once, but that deletion does not make later upstream +changes to those paths go away. Measured, not assumed: + +| Upstream does | `git merge upstream/main` does | +|---|---| +| modifies a page you deleted | `CONFLICT (modify/delete)` - and **leaves the upstream version in your working tree**. Resolve it with `git add -A` and the demo page is back. | +| adds a new page | stages it **silently**. No conflict, no prompt, no mention. | +| deletes a page you also deleted | nothing. The only harmless case. | + +The middle row is the one that matters, because nothing announces it. An upstream that ships a +demo corpus *and* uses it as a test bed will add pages, and each one arrives in your instance +and starts showing up in your `lint`, your `index`, your `search` and your `confidence decay`. + +So the merge has to be scoped. That is the procedure below, and it is not optional. ## Steps @@ -87,15 +102,43 @@ without asking. Stack changes land as real merges, with real conflicts where the ## Taking a stack update +Take the machinery, never the content. The merge is held open, the content stages are forced +back to your own state, and only then does it close: + ```bash +BEFORE=$(git rev-parse HEAD) git fetch upstream -git merge upstream/main + +# --no-commit holds the merge open; it may report conflicts under kb/ or raw/, +# which the next three lines are about to make irrelevant. +git merge --no-commit --no-ff upstream/main || true + +# Whatever the merge did to the content stages, undo it. HEAD is still your +# pre-merge commit while the merge is open, so this restores exactly your side. +git rm -rq --cached --ignore-unmatch kb raw +rm -rf kb raw +git checkout HEAD -- kb raw + +git commit --no-edit ``` +Then **check that it worked**, rather than trusting that it did: + +```bash +git diff --name-only $BEFORE HEAD -- kb raw # must print nothing +``` + +An empty result is the proof that the update touched machinery only. A non-empty one means a +path slipped through - inspect it before going further. + Then, as after any stack change: `doctor`, `docs verify`, `instructions verify`, `migrate status`, `lint`. A `migrate status` with outstanding links means the update crossed a compatibility boundary - follow [migrate-corpus.md](migrate-corpus.md) before doing anything else. +**Why not just `git merge upstream/main`?** Because of the table above: a page the upstream +*adds* arrives with no conflict and no message. You would find out when `lint` starts reporting +pages you never wrote - if you noticed at all. + ## Where stack development happens **In the public repo, not here.** That is not a preference; the stack is built that way. The @@ -110,9 +153,12 @@ merge above. Nothing is lost by the detour: the fix has to pass that CI either w ## Decision points -- **Merge conflict in `kb/` or `raw/`?** Something changed the upstream's corpus after you cut - it. Resolve as "keep deleted" - your instance's content is yours, and the upstream's demo - corpus has no business in it. +- **Merge conflict in `kb/` or `raw/`?** Expected, and already handled: the update procedure + above overwrites those stages with your own afterwards, so the conflict resolves itself. + Never resolve one by hand with `git add -A` - that is exactly how the upstream version, which + git left sitting in your working tree, gets committed into your instance. +- **`git diff` after the merge shows something under `kb/` or `raw/`?** Stop. The scoping step + did not take. Do not publish; find out which path came through and where from. - **Conflict in `tools/`, `types/` or `instructions/`?** You changed the stack locally, which step "Where stack development happens" says not to do. Take the upstream side and re-file the change as an issue there.