obsidian-untangle-knot
Базовая идея
На общие MOC ссылаются десятки заметок: проще сослаться на [[DevOps]], чем искать подкарту. In-links растут стихийно, out-links — медленно, поэтому сигнал клубка — высокий in-degree, не out.
Скилл считает in/out хаба, использует существующую структуру hub (подзаголовки, категории) как готовые точки приземления, создаёт под-MOC и — главное действие — переключает входящие ссылки ресурсных заметок на подходящий под-MOC. Оригинал остаётся точкой входа «не знаю точнее куда»: не удаляем, один hub за прогон, файлы между папками PARA не двигаем — перевязываем связи, а не структуру.
Правила хранилища — .agents/rules/, загружаются автоматически. obsidian-split-note разбивает содержимое заметки на атомарки; здесь разбиваем связи — атомарки не создаются.
Алгоритм
1. Выбор hub-заметки
Точечный — пользователь назвал заметку: fd -t f "DevOps.md" .; несколько совпадений — спроси. Discovery — «найди мои hub-заметки»: прогон по 03. Ресурсы/07. Карты/ (или другой папке), top-10 по in + out, спроси какую разгружать:
for f in "03. Ресурсы/07. Карты/"*.md; do
out=$(rg -o '\[\[[^\]]+\]\]' "$f" | wc -l); echo "$out $f"
done | sort -rn | head -10
rg -c -F "[[Имя заметки]]" . # in-links
2. Подсчёт ссылок и диагностика
In-links — три потока с разной стратегией:
| Поток | Что это | Перепривязка |
|---|---|---|
| A. Out-соседи hub | Заметки, на которые ссылается hub (тело или down) |
Итерация 1 |
| B. Up-children | Заметки с up: [[Hub]], не упомянутые в теле hub |
Итерация 1 — не пропускай, самая «забытая» нагрузка |
| C. In-links в телах | Упоминания [[Hub]] в телах (rg -l -F, плюс pipe-вариант `[[Hub |
`) |
Поток B ищи только по полю up: — грубый grep ловит [[Hub]] в other/links/category/sources, а это другая семантика. Точный парсинг:
python3 - <<'PY'
import os, re
n = 0
for dp, _, fns in os.walk('.'):
for fn in fns:
if not fn.endswith('.md'): continue
try: c = open(os.path.join(dp, fn), encoding='utf-8').read(4000)
except: continue
m = re.match(r'^---\n(.*?)\n---', c, re.DOTALL)
if not m: continue
fm = m.group(1)
b = re.search(r'^up:\s*\n(.*?)(?=^[a-zA-Z_]+:|\Z)', fm, re.M|re.S)
s = re.search(r'^up:\s*(.+)$', fm, re.M)
if (b and re.search(r'-\s*"?\[\[Hub\]\]"?', b.group(1))) or (s and '[[Hub]]' in s.group(1)): n += 1
print(n)
PY
Поля frontmatter: трогаем только up (поток B) и down hub-а. other, links, category, sources (никогда) — не трогаем.
Порог по in-degree: ≥ 100 — распутываем; 50–100 — спроси; 20–50 — прирост невелик; < 20 — рано. Out-degree: N < 5 — лепить не из чего, откажись; 5–10 — 2–3 категории, если намечены подзаголовками; > 10 — нормальная категоризация.
3. Чтение соседей (без изменений)
Для каждого out-соседа — короткий read (frontmatter + первая осмысленная строка): существует ли файл (dangling — не сосед, в отдельный список), тезис, структурный тег, confidence, и главное — значение up (определяет ветку в 6.2).
Типы: атомарка/MOC — стандартная перепривязка (MOC также в down под-MOC); упоминание (#person, компания, инструмент) — не категоризуется, остаётся контекстной ссылкой в hub; 02. Сферы/05. Медийность/ — свой жизненный цикл, up не трогаем; сосед с up на существующий альтернативный под-MOC — уже категоризован, оставляем.
4. Категоризация
Под-MOC — точки приземления будущих in-links. Есть ##-подзаголовки — используй их как готовые категории, не выдумывай оси: сохраняй ментальную модель автора. Нет — подбери ось: домен знаний, уровень абстракции (принципы/инструменты/опыт/источники), тип артефакта, зрелость (confidence), существующие теги.
Размер: минимум 2 заметки, если категория намечена структурой; минимум 3 — если выдумана; > 20 — дроби внутри (Hub → группа → под-MOC). Одиночки — в «Прочее» hub-а, без под-MOC. Имя — claim-based по file-naming.md; семейство от одного родителя — <Родитель> – <Категория>.
Валидация выдуманных осей через myvault_search по соседям hub (параметры гибридного поиска — см. CLAUDE.md): в топе планируемые соседи → ось рабочая; разброс по чужим заметкам → переформулируй; пусто → категория выдумана, откажись. Дополнительный сигнал, первичен графовый анализ.
Стоп-сигнал: ни одна ось не даёт категорий → материал монолитный, это центр темы, а не клубок. Скажи прямо, предложи obsidian-note-critic или obsidian-split-note.
5. План разгрузки → подтверждение
Две итерации; реальное падение in-degree приходит только во второй — не обещай чудес после первой.
Итерация 1 (каркас, безопасная): создать под-MOC; перепривязать up у потоков A и B (объединяются; при up-children > 50 — батчи по 10–20); минимально обновить тело hub. Итерация 2 (опциональная): перенаправить поток C — нужен контекст каждой ссылки; применимо только где [[Hub]] — центральная тема заметки; может оказаться почти не нужной.
План: числа in/out, dangling out-links, соседи на альтернативных картах, создаваемые под-MOC с числом заметок, число перепривязок, действия с телом hub, ожидаемый эффект по итерациям. Подтверждение на каждую итерацию отдельно; итерации не объединять.
6. Выполнение
6.1. Создать под-MOC
В 00. Входящие/ по Шаблон карты.md (после ревью → 03. Ресурсы/07. Карты/), файл заводи через Templater — см. навык /note-templates, раздел «Как применить шаблон технически». Wikilink резолвится по имени файла — перепривязка работает, пока под-MOC в инбоксе. Блок ниже — что должно получиться после заполнения:
---
aliases: []
tags:
- MapOfContent
- review
up:
- "[[Оригинальный hub]]"
down: []
links: []
other: []
contradicts: []
summary: "Какие темы собирает под-MOC и для какой задачи — 1–3 предложения, plain text"
ai_generated: true
---
Тело: описание категории (1–2 предложения), ## Заметки (ссылки с аннотациями), ## Связанные карты (соседи-MOC; сюда и в other — обнаруженный альтернативный под-MOC: не дублируем чужую категоризацию, а ссылаемся).
Описание категории — именно 1–2 предложения: под-MOC это навигация, а не пересказ собранных заметок (note-density.md). Аннотация к ссылке отвечает «зачем сюда идти», а не повторяет summary заметки. После создания — python3 .agents/scripts/check_note_density.py "00. Входящие".
6.2. Перепривязать up у соседей — ветки
upсодержит[[Hub]]→ заменить на[[Под-MOC]]; других родителей не трогать.upпустой → добавить[[Под-MOC]](не hub).upсодержит dangling wikilink → намерение автора категоризовать: замени dangling на актуальный под-MOC.upсодержит существующий альтернативный под-MOC → не трогаем; в плане «остаётся на альт. карте», альтернатива — вother/«Связанные карты» нового под-MOC.- Сосед-упоминание (
#person, компания, инструмент) →upне трогаем. - Telegram/контентные (
02. Сферы/05. Медийность/) →upне трогаем. - Есть более точный родитель,
[[Hub]]лишний (напримерup: [[SRE]], [[DevOps]]у заметки про SRE) → удалить hub изup, не перепривязывать. Признак: ни один под-MOC не подходит семантически, а другой родитель точнее. Не путать с альтернативным под-MOC: там другая категоризация той же темы, здесь — другая тема.
Попутно: dangling wikilinks старой категоризации в телах можно заменить на актуальные. Сохраняй YAML-формат исходника. sources не трогаем; меняешь up литературного источника — синхронизируй по политике «up + sources». links/other не очищаем — замене подлежит только up.
6.3. Перенаправить in-links в телах (итерация 2)
rg -l -F "[[Имя hub]]" --type md . → перепривязываем только ресурсные папки (03. Ресурсы/01…05, 07). Не трогаем 01. Проекты/, 02. Сферы/, 05. Дневник/, 04. Архив/, 00. Входящие/ — там ссылки на общий MOC намеренны (точка входа, исторический контекст); инбокс — для obsidian-refactor-inbox.
Для каждой заметки: контекст ссылки (1–2 строки вокруг) → категория определима — замени на [[Под-MOC]]; нет или контекст общий — оставь оригинал (это правильно). Несколько ссылок в заметке — по отдельности, могут уйти в разные под-MOC. Не трогай ссылки в callout-блоках (> [!quote], > [!note], «Ревью») и в sources. Спорные — оставь оригинал, в «Под вопросом».
6.4. Минимально обновить тело hub
Не переписывай целиком. Есть подзаголовки — первой строкой после каждого вынесенного добавь pointer → Подкарта: [[DevOps – Управление инфраструктурой]]; существующие out-links под заголовком не трогай. Плоский список — сгруппируй по категориям, сохраняя порядок и формулировки. Frontmatter hub-а: в down добавить под-MOC; up, aliases, links, other, tags, Bases/Dataview — не трогать.
6.5. Заполнить down у под-MOC
Списком заметок, у которых теперь up: [[Под-MOC]] — из плана, без повторного скана.
7. Отчёт
После итерации 1: in до/после (точные и pipe-варианты отдельно), созданные под-MOC с их in, число перепривязанных up, «Под вопросом» — и обязательное предупреждение: in-degree упал слабо, перепривязан только frontmatter, реальный эффект — после итерации 2. Затем вопрос: идти ли во вторую. После итерации 2: падение in, разбивка «N из M» по папкам, что сохранено as-is, «Под вопросом» с выбором по каждой. Выявилось новое (соседний hub, противоречие соседей, дубликаты) — упомяни, не лезь в той же итерации.
Особые случаи
Concept-hub (атомарка обросла in-links, типа Kubernetes) — спроси пользователя при выборе hub: (1) превратить в MOC — #thought → #MapOfContent, frontmatter по Шаблон карты.md, переместить в 03. Ресурсы/07. Карты/, backlinks Obsidian обновит; (2) оставить концептом и создать Kubernetes – MOC.md рядом — in-links идут на MOC, концепт остаётся узким «определение/идея».
Out-клубок (низкий in, высокий out — свежесобранная карта): перепривязка не нужна; главное — переписать тело hub в оглавление по под-MOC. Спроси, какой эффект нужен (навигация vs graph view).
Двусторонние ссылки (заметка и в up, и в down оригинала) — симптом запутанности: покажи явно, спроси какое направление оставить; по умолчанию общая — в up, частная — в down.