feat: wikitool move - eine kb-Seite folgt ihrem Subtype ins berechnete Verzeichnis (#56)
CI / verify (push) Successful in 58s
Release / release (push) Successful in 35s

Files changed:
- CHANGES.md
- VERSION
- instructions/page-lifecycle.md
- instructions/publish-cycle.md
- tools/CONTRACT.md
- tools/chemenu/cli.py
- tools/chemenu/commands/log_append.py
- tools/chemenu/commands/migrate_cmd.py
- tools/chemenu/commands/new_page.py
- tools/chemenu/commands/page_ops.py
- tools/chemenu/corpus_diff.py
- tools/chemenu/lint_core.py
- tools/chemenu/tests/test_corpus_diff.py
- tools/chemenu/tests/test_lint.py
- tools/chemenu/tests/test_migrate_cmd.py
- tools/chemenu/tests/test_page_ops.py
- tools/chemenu/tests/test_type_resolver.py
- tools/chemenu/type_resolver.py
This commit is contained in:
2026-09-04 22:50:22 +02:00
parent 8e25a7865f
commit 9e414319b8
18 changed files with 615 additions and 54 deletions
+57 -1
View File
@@ -35,12 +35,13 @@ dev-checkout concern - readable here, never shipped as something to parse.
---
## 4.7.5-beta.1 - 2026-09-04 - status/incoming: menschliche Stubs werden ausgearbeitet, nie so umgesetzt
## 4.8.0-beta.1 - 2026-09-04 - page move: eine kb-Seite folgt ihrem Subtype ins Verzeichnis, das ihr Type-Spec berechnet
**Author:** Torben Nehmer
<!-- wikitool:bumps -->
- status/incoming: menschliche Stubs werden ausgearbeitet, nie so umgesetzt
- page move: eine kb-Seite folgt ihrem Subtype ins Verzeichnis, das ihr Type-Spec berechnet
<!-- /wikitool:bumps -->
Das Label `status/incoming` gibt es seit heute in Gitea: der Mensch legt einen
@@ -95,6 +96,61 @@ Kernpunkt dazu, dass dieses Flag die Vier-Achsen-Pflicht aussetzt.
Offen bleibt die erste tatsächliche Anwendung der Regel: #60 und #61 sind
weiterhin unausgearbeitete Stubs.
**`wikitool move`** (#56): Nichts im Stack bewegte bisher eine Seite über eine
Verzeichnisgrenze — `rename` schreibt laut eigenem Docstring nie über
`target.path.parent` hinaus, und `new_page._target_dir` rechnete die
Platzierung nur beim Anlegen. Ändert sich `entity_type` später, blieb die
Datei still am alten Ort liegen, und nichts prüfte das nach: weder
`lint_core.py` noch `doctor.py` verglichen den Ist-Ort einer Seite mit dem,
was ihr Type-Spec berechnen würde.
Die Platzierungslogik selbst gibt es jetzt genau einmal:
`TypeResolver.compute_target_dir` (plus `TypeResolver.subtype_dir` für den
`layout:`-Teil), `new_page._target_dir` delegiert nur noch dorthin. Darauf
aufbauend zwei neue Stücke:
- `wikitool move --page "<Titel>"` bewegt eine Seite an den berechneten Ort;
`--reconcile` wendet dieselbe Regel auf den ganzen Bestand an und ist
idempotent (ein zweiter Lauf meldet nichts mehr zu tun). Beide Modi fassen
weder Body noch Frontmatter an, und der Titel — die einzige Identität einer
Seite — ändert sich nie, also folgt kein Referenz-Update. Ein bereits
belegtes Ziel (ein alter Stem-Kollisionsrest) wird verweigert statt still
überschrieben. Top-level registriert, wie `rename`/`rm`, nicht unter einem
`page`-Unterbefehl.
- `lint` bekommt einen neuen Befund, **Misplaced Pages**, mit Ist- und
Soll-Pfad. Bewusst nicht in `HARD_ERROR_KEYS`: ein von Hand platzierter
Bestand ist keine kaputte Seite, nur eine, die `move` aufräumen könnte — auf
dieser Instanz sind das aktuell die drei `kb/entities/projects/*/`-Seiten,
die #57 separat behandelt.
Ein Fund unterwegs, der ohne #56 unsichtbar geblieben wäre: `migrate verify`
schlüsselte Seiten über den repo-relativen **Pfad**. Ein reiner Move ergab
„N removed, N added, 0 compared" und lief grün durch — der eine mechanische
Check, für den `instructions/migrate-corpus.md` existiert, hätte bei genau der
Operation nichts geprüft, die dieses Issue einführt. Behoben: `PageShape`
trägt jetzt zusätzlich den Pfad, `_shapes_at_revision`/`_shapes_now`
schlüsseln über den Titel (die einzige Identität einer Seite), und
`CorpusDiff.moved` meldet einen reinen Ortswechsel separat — informativ,
niemals als Finding. Regressionstest deckt drei verschobene Seiten mit
`compared == 3, added == 0, removed == 0` ab.
Bewusst nicht Teil dieses Pakets: die drei realen Seiten aus #57 bleiben
liegen (kein Korpus-Publish hier, nur Stack), und `raw rename` (#16) — das
`git mv`-mit-`mv`-Fallback aus #56s Entwurf war für Rohdateien gedacht;
`rename`/`rm`/`move` bewegen kb-Seiten über ein einfaches `Path.rename`, weil
`publish` ohnehin über `git add -A` staged.
Geändert: `tools/chemenu/type_resolver.py` (`compute_target_dir`,
`subtype_dir`), `tools/chemenu/commands/new_page.py` (delegiert),
`tools/chemenu/commands/page_ops.py` (`move_command`),
`tools/chemenu/lint_core.py` (`find_misplaced`, `misplaced_pages`),
`tools/chemenu/corpus_diff.py` und `tools/chemenu/commands/migrate_cmd.py`
(Titel-Schlüsselung, `moved`), `tools/chemenu/commands/log_append.py` (`--op
move`), `tools/CONTRACT.md`, `instructions/page-lifecycle.md`,
`instructions/publish-cycle.md`. MINOR: rückwärts liest ein älterer Stack eine
verschobene Seite unverändert (Identität ist der Titel, nicht der Ort),
vorwärts reines Überkopieren.
---
## 4.7.4 - 2026-09-04 - bootstrap.md nennt den session-id-WARN nach frischem Bootstrap explizit als erwartet