[2026-09-15] eagle: family/how-to/truenas-infrastructure.md family/how-to/truenas-infrastructure.md.bak-before-drop-changelog family/how-to/truenas-infrastructure.md.bak-before-merge-dup-header personal/tech/vault-doc-pruning.md personal/tech/vless-space-subscription-egress.md personal/tech/vless-space-subscription-egress.md.bak-before-squeeze-history personal/tech/vless-space-subscription-egress.md.bak-before-squeeze2 personal/tech/xray-outbound-subscription-3xui.md

This commit is contained in:
Alexey Martemyanov
2026-09-15 17:45:27 +06:00
parent 548bedf3cd
commit 397847b50b
8 changed files with 3428 additions and 349 deletions
+149
View File
@@ -0,0 +1,149 @@
---
title: Чистка устаревших разделов в доках vault — метод
created: 2026-09-15T00:00:00.000Z
updated: '2026-09-15T23:59:00.000Z'
type: tech
namespace: personal
tags:
- obsidian
- docs
- vault
- methodology
- pitfalls
- unicode
confidence: high
status: done
related:
- '[[family/how-to/truenas-infrastructure]]'
- '[[personal/tech/vless-space-subscription-egress]]'
- '[[personal/tech/xray-outbound-subscription-3xui]]'
---
# Чистка устаревших разделов в доках vault — метод
> ## 🎯 Когда применять
>
> Док накопил **летописи** — разделы вида «ПОПЫТКА 1…7», «ПРЕДЫДУЩИЙ ФИНАЛ: ЗАДАЧА НЕ ВЫПОЛНЕНА», «Волна 1 правок». Они описывают состояние, которое **уже противоречит факту**, и читаются как текущий статус. Alex: «Там никаких летописей не осталось точно?! Только статус и инструкции??»
>
> **Целевая форма дока:** статус → что хотели → как работает сейчас → процедура → питфоллы → артефакты. Грабли — **двумя строками**, не разделом.
---
## 🔴 Главное правило: вырезать блок ≠ задача выполнена
**Ошибка этой сессии:** вырезал блок-летопись из 163 строк и отчитался «готово». Через ход Alex спросил про остатки — grep нашёл **ещё 6 устаревших мест** в том же доке, включая простыню «`vless-space` НЕ СОЗДАН, БД ОТКАЧЕНА» на строке 101.
**Почему так вышло:** фокус на одном блоке, ноль проверки остального файла. В доке 990 строк — устаревшие утверждения живут не только в летописях, но и в шапках, таблицах, «Открытых вопросах».
**Обязательный шаг после любого вырезания — grep по СМЫСЛУ, а не по имени блока:**
```bash
grep -rn "НЕ СОЗДАН\|НЕ ВЫПОЛНЕН\|откачен\|НЕ ЗАЛИТ\|следующий шаг\|не начат" \
--include="*.md" family/ personal/ | grep -v "\.bak"
```
Плюс пройти по **всем связанным** докам (в этой задаче их было 5), а не только по тому, где нашёл проблему.
---
## 🔴 ПИТФОЛЛ 1: маркер не находится из-за нормализации Unicode (NFC/NFD)
**Симптом:** скрипт падает на `assert end marker not found`, хотя строка в файле **визуально** точно такая, как в коде.
**Причина:** macOS HFS+/APFS отдаёт имена/строки в **NFD**, Obsidian и git обычно пишут **NFC**. Русские буквы с диакритикой (`й`, `ё`) и эмодзи (`⚠️`) в двух формах — это **разные байты**.
**Диагностика — сравнить длину:**
```bash
awk 'NR==550' file.md | tail -c 200 | cat -v # покажет \M-^M и прочие артефакты
```
Если в конце видно `\M-^...` — строка не в той нормализации, что литерал в скрипте.
**Лечение — не искать по точной строке целиком, а матчить по НАЧАЛУ или ХВОСТУ:**
```python
# ❌ хрупко: ломается на нормализации и на любом изменении хвоста
END_MARK = "отдельный кусок, не начат."
if END_MARK in ln: ...
# ✅ устойчиво: хвост без диакритики, проверяем через endswith по rstripped строке
END_TAIL = "не начат."
if ln.rstrip().endswith(END_TAIL): ...
```
Ещё надёжнее — маркер **без** диакритики/эмодзи (ASCII или простые кириллические буквы без `й`/`ё`).
**Проверка нормализации файла:**
```bash
python3 -c "import unicodedata,sys; s=open(sys.argv[1]).read(); print('NFC' if unicodedata.is_normalized('NFC',s) else 'NFD-или-смешанное')" file.md
```
---
## 🔴 ПИТФОЛЛ 2: вырезал блок → украл заголовок секции
**Симптом:** после вырезания раздела в доке **пропал подзаголовок** `### xray-admin — 3x-ui панель`, и секция стала сиротой в оглавлении.
**Причина:** заголовок жил **внутри** вырезанного диапазона (он был частью летописи), а следующий за ним текст — уже живая часть дока.
**Ошибка усугубилась:** я «восстановил» заголовок **вслепую**, не проверив, есть ли он ниже. В результате заголовок оказался **дважды** (строки 333 и 399), и настоящая секция с таблицей параметров шла второй.
**Правило:** перед вырезанием — `grep -n "^#\{1,4\} " file.md` и записать, какие заголовки внутри диапазона. После вырезания — **проверить дубли**:
```bash
grep "^### " file.md | sort | uniq -d # пусто = дублей нет
```
Проверка стыка глазами — обязательно (читаем 10 строк до и 10 после места реза).
---
## 🧱 Процедура (по шагам)
1. **Снять границы.** `read_file` с `offset`/`limit` вокруг блока; `grep -n "^#"` по файлу — полная карта заголовков.
2. **Классифицировать каждый раздел.** «Это статус/инструкция» → оставить. «Это рассказ о том, как я ошибался» → вырезать, **извлекая грабли**.
3. **Извлечь грабли в 1–2 строки** перед вырезанием (не после — иначе потеряются).
4. **Написать скрипт-резак** в `~/tmp-xray-space/`, не sed. Бэкап — первой операцией:
```python
shutil.copy2(SRC, SRC.with_suffix(".md.bak-before-<что-делаем>"))
```
5. **`assert` на каждый маркер** + на порядок маркеров (`start < end`). Скрипт обязан упасть, если разметка не та.
6. **Прогнать, проверить стык**, grep на остатки + дубли заголовков.
7. **Пройти по связанным докам** — тем же grep'ом на смысл.
**Почему не `sed`:** прямое указание Alex — «NEVER use in-place scripts or SED for editing». Плюс sed не даёт `assert` и молча «съест» не тот диапазон.
---
## ✅ Чек-лист приёмки
- [ ] Летописей нет: `grep -rn "ИСТОРИЯ\|ПОПЫТКА\|ПРЕДЫДУЩИЙ\|Волна" <файлы>` → только не относящиеся к теме
- [ ] Дублей заголовков нет: `grep "^##\+ " f.md | sort | uniq -d` → пусто
- [ ] Стыки читаются (10 строк до/после)
- [ ] Wiki-ссылки живы
- [ ] Бэкап `.bak-before-*` на месте
- [ ] Связанные доки проверены, не только целевой
> ⚠️ **Проверка wiki-ссылок — только относительным путём.** `[[obsidian-sync]]` — это `family/how-to/obsidian-sync.md`, не `obsidian-sync.md` в корне. Проверка «файл существует по литеральному имени» даёт **ложные срабатывания**:
> ```bash
> for n in $(grep -o "\[\[[^]]*\]\]" f.md | sed 's/.*\[\[//;s/\]\]//;s/|.*//'); do
> find . -name "$n.md" -not -path "./.git/*" | head -1
> done
> ```
> Найдено «5 битых ссылок» → все 5 существовали в других папках. **Не паниковать до `find` по всему vault.**
---
## 📉 Эффект (факт, 2026-09-15)
| Файл | Было | Стало | Что убрано |
|---|---|---|---|
| `personal/tech/vless-space-subscription-egress.md` | 854 | 552 | 8 разделов-летописей → 2 строки граблей |
| `family/how-to/truenas-infrastructure.md` | 990 | 844 | блок 163 стр. + чейнджлог моих правок + дубль заголовка |
---
## 🧱 ГРАБЛИ — 3 пункта (все поймал на себе)
1. **Вырезал один блок — проверь весь файл и все связанные доки.** Летописи живут не только в очевидных местах; устаревшее утверждение может сидеть в шапке или в таблице.
2. **Маркер для вырезания — без диакритики и эмодзи, матч по `startswith`/`endswith`.** Точное совпадение строки ломается на NFC/NFD (macOS отдаёт NFD).
3. **Заголовки внутри вырезаемого диапазона — зафиксировать ДО реза.** Восстановив заголовок вслепую, получишь дубль; проверять `sort | uniq -d`.
## Связанные заметки
- [[family/how-to/truenas-infrastructure]] — где применялся метод (блок `xray-admin`)
- [[personal/tech/vless-space-subscription-egress]] — где применялся метод (летописи попыток)
- [[personal/tech/xray-outbound-subscription-3xui]] — техническая часть той же задачи