Русский
ZIP, который тихо терял папку .obsidian
Стартовый архив, который вы скачиваете из trip2g, — это ZIP. Месяцами он распаковывался нормально. Потом спешная правка поменяла способ сборки и тихо сломала распаковку папки .obsidian — и консольный unzip, и штатный распаковщик macOS её калечили. Хранилище всё равно доставалось с заметками, только без папки с плагином синхронизации — и оно молча не синхронизировалось. Так оно и пролежало в основной ветке пять дней, пока я не наткнулся. Разбираем, что сломалось, почему пять дней и как я это починил.
В истории несколько поворотов. Поломка была тихой — вы получали хранилище, которое выглядело нормально и просто не синхронизировалось. Правка, из-за которой он появился, сама была фиксом другого бага. А ещё это одна строка внутри шестинедельного спринта на $22k — и отсюда вопрос покрупнее: что будет с числом багов, когда ИИ пишет в десять, а потом в сто раз больше кода.
Экран ошибок
Стартовый архив отдаётся в виде ZIP. Однажды я распаковал его из терминала, и он заругался:
checkdir error: .../.obsidian exists but is not directory
unable to process .obsidian/app.json
Такая ошибка повторялась на каждом файле внутри .obsidian/. Остальной архив распаковывался, но папка .obsidian с плагинами не создавалась — а без плагина синхронизация не работала.
Причина: пропущенный слэш
ZIP хранит папку отдельной записью с завершающим слэшем: .obsidian/. Упаковщик — Python-скрипт в onboarding-vault/generate.sh — писал записи папок без слэша, просто .obsidian.
Без слэша это запись нулевого размера. Консольный unzip (Info-ZIP) читает её как обычный файл с именем .obsidian. Дальше он доходит до .obsidian/app.json, пытается создать app.json внутри того, что уже посчитал файлом, и сдаётся: exists but is not directory.
Откуда это взялось: фикс, который привёз регрессию
Раньше архив паковала системная команда zip. Она пишет правильные записи папок со слэшем, поэтому unzip был доволен месяцами — но она не ставит в ZIP флаг UTF-8 для имён файлов, и Windows раскодировал русские имена заметок своей старой кодовой страницей и портил их.
Поэтому я поменял zip на Python-упаковщик zipfile — ровно чтобы это починить. Python пишет имена в UTF-8 и ставит флаг сам, и Windows снова был доволен. Та же правка заодно писала записи папок без завершающего слэша. Один фикс и одна свежая регрессия в одном коммите.
Почему заметил только через пять дней
Битый архив пролежал в основной ветке пять дней. По пути от коммита до скачивания его никто не распаковывает заново и не проверяет результат — ни сборка, которая его пересобирает, ни CI, ни я. И поломка тихая: хранилище всё равно достаётся с заметками, просто без .obsidian, и потому молча не синхронизируется.
Так что дело не в том, что снисходительные инструменты его прощали, — его калечил любой распаковщик, и терминал, и macOS. Дело в том, что за пять дней свежую сборку скачивают немногие, а те, кто скачал, получили хранилище, которое выглядело нормально, — и ничто на пути не распаковывает архив, чтобы проверить. Я поймал баг, когда распаковал один сам на пятый день.
Как починил
Записи папок нужно писать с завершающим слэшем. Одна небольшая правка в упаковщике:
# было
info = zipfile.ZipInfo(str(rel))
# стало
info = zipfile.ZipInfo(str(rel) + "/")
Go-код отдачи копирует записи архива без изменений, поэтому починка исходного ZIP чинит каждое следующее скачанное хранилище. Фикс: c7b305a8.
Тесты-страховки
Теперь за этим следят два теста. Чистый Go-тест проверяет правило прямо на сгенерированном архиве: ни у одной записи-файла не может быть вложенных записей — если существуют и X, и X/..., то X обязан заканчиваться на /. Он быстрый и ничего внешнего не требует. Второй проще и грубее: дёргает unzip, распаковывает архив и падает на первом же предупреждении — исходный баг, воспроизведённый целиком. Оба падают на старом битом ZIP и проходят на исправленном. Коммиты: 85a1d913, 0e66cf1b.
Вывод
Эти пять дней — не про плохую модель и не про плохой процесс. Похоже, это просто статистическая погрешность: при таком объёме кода какие-то дефекты неизбежно проскакивают, и один из них прожил пять дней. Тест-страховка выше этот фон не отменяет — только снижает: она распаковывает и проверяет архив на каждой сборке, а не ждёт, пока кто-то распакует его руками. В этом весь вывод, и он скромнее, чем «берите инструменты получше»: проверяйте то, что отдаёте, автоматически и каждый раз.
Счёт за марафон
Эта правка прилетела во время большой гонки Fable до 7 июля — спешил, набивал шишек, чинил одно и задевал другое. У спринта остался бумажный след. bunx ccusage claude weekly показывает, сколько Claude Code сжёг по неделям:

Сверху вниз — это и есть марафон. Недельный счёт растёт почти в десять раз за неделю, упирается в ~$9k на неделе 28 июня и падает вдвое-втрое на следующей — когда я перестал спешить. Шесть недель, 26,5 миллиарда токенов, около $22k. 96% из них — чтение кэша: модель перечитывает большие контексты, а не пишет новый код. Этот баг с ZIP — одна строка в этом счёте.
Мораль
ИИ вспахивает всё поле. За неделю он поднимает больше земли, чем я за сезон, — и именно поэтому потом столько всего разгребать. Пропущенный слэш был одним комом. Сейчас я вышел из режима кранча и остальное буду разбирать ещё какое-то время.
Вопрос покрупнее, догадка
Этот баг не особенный — в этом и суть. При равном объёме ИИ, кажется, ошибается примерно столько же, сколько я, — не лучше и не хуже. Изменился объём. Кода стало примерно в десять раз больше, чем год назад, и станет в сто раз больше. Оставьте уровень ошибок прежним, умножьте объём на сто — и багов станет в сто раз больше.
Модели учились на человеческом коде, который писали люди, никогда не работавшие в таком масштабе, — где абсолютное число дефектов держалось ниже критического. Тот же уровень ошибок на куда большем объёме эту границу переходит. Поэтому планка качества кода растёт ей навстречу — и уже растёт. Компании начинают отказывать, если вы не пишете с ИИ: им нужна производительность, и они ждут, что ошибки удержат на приемлемом уровне.
Если это так — если ИИ при том же объёме даёт примерно те же ошибки, что и человек, — тогда скорость реальна, а дисциплина должна расти вместе с ней: строже инструменты, больше тестов-страховок, больше разгребания поля потом. Это догадка. Узнаем в следующем году.
Вы можете навайбкодить свой trip2g, в своём стиле — и соберёте ровно столько же багов, тоже в своём стиле. Это весело. Не всегда. Тест добавлен, опыта прибавилось, двигаемся дальше.