Skip to content

Repository files navigation

null

Status: active

Личный статичный архив математических объектов, эссе и алгоритмических скетчей.

Структура

null/
├── index.html              сетка карточек разделов
├── styles/main.css         единственная таблица стилей
├── objects/                134 объекта: числа, идеи, нотация, статистика
├── essays/                 110 эссе
├── visuals/                36 интерактивных визуализаций
├── books/                  16 книг
├── music/                  10 альбомов
├── man.html                руководство в формате man-page
├── map.html                граф связей
├── find.html               поиск
├── sitemap.xml             карта сайта, генерируется
├── scripts/                update_counts.py, generate_sitemap.py,
│                           update_meta.py, preflight.py
├── data/links.json         граф: 313 узлов, 1710 рёбер
├── data/build-backlinks.js генератор блоков «упоминается в»
└── graph-health.py         диагностика графа

Числа в man.html и index.html поддерживает scripts/update_counts.py — править их руками не нужно.

scripts/update_counts.py про essays/index.html не знает вообще. Оба числа секции — essay-section-count в заголовке и card-num в карточке категории вверху страницы — правятся руками при добавлении эссе. Забыть второе легко: расхождение видно на странице, но ничего не ломает, и потому живёт долго.

Счётчики в essays/index.html

Три числа рядом никогда не совпадают, и это конвенция, а не ошибка:

что считаем на август 2026
записей essay-item в индексе 118
сумма счётчиков секций 114
файлов *.html в essays/ 116

Расхождение сверху дают кросс-ссылки — записи, ведущие на эссе, которые живут внутри страниц объектов. Их четыре:

  • ../objects/numbers/constants/pi.html#essay — §07
  • ../objects/numbers/constants/phi.html#essay — §07
  • ../objects/numbers/constants/euler.html#essay — §07
  • ../objects/statistics/inference/cuped.html#essay — §06

В индексе они выставлены, в счётчик секции не входят — счётчик считает файлы в essays/. 118 − 4 = 114.

Заводя новую кросс-ссылку, счётчик секции и card-num не трогай: запись прибавляется, число эссе — нет.

Расхождение снизу дают две редирект-заглушки, оставшиеся после переименований (two-systems.html, cognitive-biases.htmloshibki-po-pravilam.html). count_pages в scripts/update_counts.py их отсекает — см. is_redirect. 116 − 2 = 114.

Инвариант, который должен держаться: сумма счётчиков секций равна числу не-редиректных файлов в essays/, а в каждой секции счётчик в заголовке равен числу в карточке категории. Оба проверяет scripts/preflight.py.

<title> и h1 — разные строки, и это не дефект

<title> — служебная строка: вкладка браузера, выдача поисковика и og:title в превью ссылки. Она короткая и держит суффикс — null/essays, из которого scripts/update_meta.py берёт тему.

h1 — заголовок страницы для читателя. Он может быть длиннее, типографски полнее и иметь другую интонацию.

Расхождение между ними нормально и дефектом не считается. На август 2026 их 16 из 110: шесть отличаются только регистром и точкой, шесть — h1 продолжает тему после запятой («xG» против «xG, или счёт врёт»), четыре сформулированы по-разному («проблемы тысячелетия» против «Семь задач. Семь миллионов долларов»).

Проверять надо другое, и это делает preflight 8: чтобы og:title, canonical и og:url совпадали с тем, что записал бы update_meta.py. Эти три сейчас сходятся на всех 110 страницах.

Кто правит файлы и кто это проверяет

Заводя новый скрипт, дописывай сюда строку. Колонка «якорь» — то, за что скрипт цепляется в файле: если якорь уедет, скрипт либо промолчит, либо запишет не туда. Колонка «надзор» отвечает на вопрос, который иначе приходится задавать каждый раз заново.

скрипт пишет якорь надзор
scripts/update_counts.py man.html, index.html — цифры 16 regex по контексту (граф связей · , card-count-num, …) preflight 3
scripts/update_meta.py essays/*.html — мета-блок <!-- meta: … --><!-- /meta -->, вставка после <title> preflight 8
scripts/generate_sitemap.py sitemap.xml целиком якоря нет, файл пересобирается preflight 2
data/build-backlinks.js страницы узлов — блок backlinks <section class="related">…</section> preflight 4 и 6
scripts/related-edges.py --apply data/links.json — рёбра <section class="related"> + <a …>…→</a> preflight 5
scripts/prose-edges.py --apply data/links.json — рёбра тело страницы за вычетом related, backlinks, series, xref, offsite preflight 9

Правила отбора надзор импортирует из самих скриптов — второй реализации быть не должно, иначе разойдутся именно они. У этого правила есть одно намеренное исключение: проверка 10 в preflight.py дублирует разметку признаков секции related нарочно. Надзор, собранный из тех же деталей, не увидит поломку детали: уедет якорь <section class="related">related-edges.py молча пропустит страницу, и проверка 5, импортирующая его же scan(), ослепнет ровно там же и отрапортует «0 ✓». Проверка 10 смотрит независимо: ищет related-links, related-link и подпись «связанное», а потом сверяет, распознаётся ли на этих страницах канонический якорь.

Читают и ничего не пишут: scripts/preflight.py, graph-health.py, оба извлекателя без --apply и шестнадцать скриптов в audit/, которые кладут CSV рядом с собой.

Порядок при правке страниц

1. правки + update_meta.py + update_counts.py
2. счётчики в essays/index.html руками (два числа на секцию)
3. git add + commit
4. generate_sitemap.py
5. git commit --amend

Пункты 3–5 именно в таком порядке: generate_sitemap.py берёт lastmod из git, и для незакоммиченной страницы даты нет — запись попадает в карту без <lastmod>. Один раз так и уехало на прод. Теперь скрипт в такой ситуации ничего не пишет и завершается с ненулевым кодом.

Правило шире, чем «при добавлении страницы»: карту надо перегенерировать после любого коммита, меняющего *.html. <lastmod> берётся из даты последнего коммита, тронувшего файл, и меняется от любой правки — включая машинную. Прогон data/build-backlinks.js не добавил ни одной страницы, но сменил дату 57 файлам, и карта ушла отдельным коммитом уже после пуша. Отставание карты от коммитов проверяет scripts/preflight.py.

Ручное и машинное разделяет время, а не коммит

Формулировка «ручное и машинное — разными коммитами» описывает результат, но не способ его получить. Способ один: закоммитить ручную правку до запуска генераторов.

Причина в том, что файл для git неделим. Если сначала поправить страницу руками, а потом прогнать data/build-backlinks.js, обе правки окажутся в одном файле, и развести их постфактум нельзя — только откатывать файл и делать заново. Коммит ручного закрывает его от машинного.

Отсюда порядок при правке графа:

1. рёбра в data/links.json + ручные правки страниц
2. git commit                       ← закрывает ручное
3. node data/build-backlinks.js
4. python3 scripts/update_counts.py
5. git commit                       ← только машинный вывод
6. python3 scripts/generate_sitemap.py
7. git commit --amend

Это та же причина, по которой generate_sitemap.py идёт после коммита: он читает состояние git, а не рабочего дерева. Разница лишь в том, что там защита от неверных данных, а здесь — от неразделимого диффа.

Перед пушем — python3 scripts/preflight.py.

google9b84a06f40ea405c.html — не удалять, не менять

Файл подтверждения владения ресурсом https://yzotop.github.io/null/ в Google Search Console. Лежит в корне репозитория, отдаётся по https://yzotop.github.io/null/google9b84a06f40ea405c.html.

Google перепроверяет подтверждение периодически, а не однократно при добавлении ресурса. Пропадёт файл — слетит подтверждение, а вместе с ним данные поиска и возможность отправлять карту сайта.

  • содержимое не менять ни на байт, включая завершающий перевод строки: его в файле нет, размер ровно 53 байта. Контроль: shasum -a 256 даёт ddcd5ce0ab04a37986ed1edaaa1b7ce1ea764c806a52f85fdc1e21af264578c1
  • не переименовывать и не переносить в подпапку: Google запрашивает точный путь
  • в sitemap.xml не добавлять. Само не попадёт: файл внесён в EXCLUDED в scripts/generate_sitemap.py. Без этой строки попадёт — генератор обходит все *.html на диске
  • токен один на аккаунт Google, а не на ресурс, поэтому файл тот же, что в репозитории davydov.my. Это нормально и так задумано

Проверка целостности:

echo "ddcd5ce0ab04a37986ed1edaaa1b7ce1ea764c806a52f85fdc1e21af264578c1  google9b84a06f40ea405c.html" | shasum -a 256 -c -

В davydov.my эта проверка стоит в CI и валит сборку при порче или пропаже. Здесь CI нет — руками, см. пункт в BACKLOG.md того репозитория.

Дизайн-система

CSS-переменные определены в :root в styles/main.css. Шрифты подгружаются с Google Fonts: Inter, JetBrains Mono, IM Fell English.

Локальный запуск

cd null
python3 -m http.server 8765

Затем открыть http://localhost:8765/

Зависимости

Сборки нет, пакетов нет, node_modules нет — страницы отдаются как есть. Внешнего кода три источника, все по CDN и без версии в репозитории:

  • Google Fonts — Inter, JetBrains Mono, IM Fell English
  • Chart.js 4.4.1 с cdnjs.cloudflare.com — 7 страниц, и ещё одна тянет его же с cdn.jsdelivr.net
  • d3 7.8.5 с cdnjs.cloudflare.com — только map.html

JS на сайте есть: собственный <script> стоит на 64 страницах из 351 — интерактивные визуализации в visuals/ живут на canvas.

Отдельно от страниц лежит data/build-backlinks.js — запускается руками через node, генерирует блоки «упоминается в» из data/links.json. Узел без входящих рёбер он пропускает целиком, поэтому выписанный руками блок у такого узла остаётся, но графом не подтверждён и на карте не виден. Вставить блок ему тоже некуда, если на странице нет <section class="related"> — он молча пропустит её при живых входящих рёбрах. Оба случая проверяет scripts/preflight.py.

visuals/monty-hall.html — исключение по вёрстке

Единственная страница-узел с самодельной оболочкой. У неё собственные <nav> и хлебные крошки с инлайн-стилями и свой футер внутри <div class="page">; отсутствуют <header class="topbar">, <div class="breadcrumb">, <main class="container">, <article> и <footer class="footer">. Общий styles/main.css подключён, секции related и backlinks — обычные.

Любая проверка, опирающаяся на эти классы, её не видит. За два захода это сработало дважды: сначала не нашлось </article>, чтобы вставить секцию, потом grep class="related" вернул ноль там, где блок был — рукописный, без класса, и в итоге на странице оказалось два блока «связанное» подряд.

Приводить к общей вёрстке не нужно: у страницы своя ширина и свой визуальный язык, перевод изменит её вид. Но при написании новой проверки по общим классам эту страницу надо держать в уме.

Без <header class="topbar"> в репозитории ещё шесть файлов, и все они законны: корневой index.html со своей вёрсткой, две редирект-заглушки в essays/, два индекс-редиректа (charts/index.html, objects/books/index.html) и прототип в tools/ под .gitignore. Страница-узел с чужой оболочкой — одна.

Подписи в related и backlinks различаются намеренно

related пишется руками, backlinks собирает генератор. Поэтому подпись одной и той же ссылки в двух местах может не совпадать, и это не рассогласование:

блок кто пишет подпись
related — «связанное» автор проза, под контекст страницы
backlinks — «упоминается в» build-backlinks.js title узла буквально

Пример: в objects/notation/alphabets/hebrew.html стоит «ZFC — независимость аксиом», тогда как title того же узла в графе — «Математика которую нельзя доказать». Копировать title в related ради единообразия не надо. Метка типа (related-link-type) в related тоже авторская и грубее машинной: широкое «объект» стоит в 275 местах там, где генератор написал бы «константа» или «теорема».

related ⊆ исходящие рёбра

Отношение между секцией и графом одностороннее:

  • ссылка в related без ребра — дефект. Страница утверждает связь, которой нет на карте и не будет в чужих блоках «упоминается в».
  • ребро без ссылки в related — норма. Секция курируется автором и показывает не всё: у активных узлов исходящих бывает под два десятка, в секции — пять-восемь.

То есть множество ссылок в related должно быть подмножеством исходящих рёбер узла, а не совпадать с ними. Автоматической проверки на это нет: backlinks держит генератор, а related — только память. На август 2026 нарушений первого рода 287 на 161 странице из 311.

About

No description, website, or topics provided.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages