Volatile memory AI vs persistent memory человека. Почему это фундаментальная асимметрия, которая определяет всё. WAL как checkpoint, не лог. Что записывать, куда, и как выглядит рабочий день в системе человек-AI.
Начнём с неудобного факта, который обычно замалчивают.
Когда объясняют проблему памяти AI новым разработчикам, те кивают и думают: «понятно, AI забывает между сессиями — значит, нужно писать спеки для AI». Это верно, но это половина правды.
Через два месяца интенсивной работы ты не вспомнишь, почему выбрал blake3 вместо SHA-256. Не вспомнишь, что та функция reconnection хрупкая. Не вспомнишь, какой из трёх подходов к верификации попробовал первым и почему отбросил.
Человеческая память — не база данных. Она реконструктивная, избирательная, оптимизирована для паттернов, но не для технических деталей трёхмесячной давности.
Поэтому правильная формулировка не «пиши спеки для AI». Правильная — «пиши для системы целиком»: AI в следующей сессии прочитает и вспомнит; ты через два месяца прочитаешь и вспомнишь; новый разработчик прочитает и поймёт. Все трое — из одного источника.
В предыдущей главе мы ввели WAL как концепцию: файл, который позволяет двум процессам (человек и AI) синхронизировать состояние между сессиями. Там же был намеренно оставлен открытым вопрос: как именно он устроен изнутри и что туда писать?
Здесь — ответ. Но сначала нужно разобраться с одним лингвистическим капканом, в который попадают почти все.
Мы называем этот файл WAL намеренно. Аббревиатура Write-Ahead Log понятна любому разработчику с первого слова — и даже небольшому AI-агенту из обучающих данных. Узнаваемое имя снижает трение при работе: не нужно объяснять концепцию с нуля.
Но наш WAL — не классический лог, и важно понимать разницу.
В базах данных WAL — это бесконечно растущая последовательность записей. Туда пишут всё подряд, никогда не удаляют, иногда компактируют. Это правильно, потому что WAL читает машина, которой нужна полная история для восстановления после сбоя.
Наш WAL читают оба — и человек, и AI. Но проектировать его нужно для человека, потому что AI адаптируется к любому формату, а человек — нет.
Представь: каждое утро ты приходишь к доске, на которой записан статус проекта. Хорошая доска — та, на которой написано три строки: где мы, что делаем, что нельзя трогать. Плохая доска — та, на которой триста строк истории за три месяца, включая давно закрытые задачи и решения, которые ревизовались пять раз.
WAL — это доска, не журнал.
Техническая аналогия точнее: checkpoint в базе данных. Checkpoint — это снимок текущего состояния. Он не накапливается — он перезаписывается. Старый checkpoint бессмысленен, когда есть новый.
Это значит одно: WAL.md перезаписывается в конце каждой сессии. Он всегда описывает только текущее состояние. Максимальная длина — одна страница. Если он вырос до трёх — он делает не ту работу.
История никуда не девается. Она просто живёт в другом месте — в milestone commits, в спецификациях, и в эпиках, о которых мы поговорим в следующей главе.
Чтобы понять что именно писать и куда, нужна карта. Всё в проекте живёт на одном из четырёх уровней:
Уровень 1: Голова разработчика
— Интуиция, вкус, контекст за пределами проекта
— Единственное место, где живут решения до их записи
— Видит только один человек, только пока он в проекте
Уровень 2: WAL
— Текущее состояние: что сделано, что делается, что дальше
— Горизонт: часы и дни
— Перезаписывается в конце каждой сессии
Уровень 3: Спецификации (PROP, FEAT)
— Стабилизированные решения и намерения
— Горизонт: месяцы
— Адресуемые через spec://..., версионированные
Уровень 4: Код
— Артефакт выполнения спецификаций
— Отвечает на вопрос «как», не «почему»
Три правила из этой карты:
Правило нисходящей записи. Информация течёт сверху вниз: решение в голове → WAL немедленно → спека когда стабилизировалось → код. Это основной поток.
Правило единственного источника. На каждый факт — один авторитетный источник.
Таймаут верификации: spec://oproto/PROP-003#verification.timeout. Не в коде,
не в WAL, не в двух местах одновременно. Дублирование — это не резервирование,
это бомба замедленного действия: версии рано или поздно разойдутся.
Правило видимости. Всё что живёт только в голове — невидимо для AI. Всё что AI «понял» в текущей сессии — исчезнет с её закрытием. Только файлы переживают обоих.
Каждый раздел WAL отвечает на конкретный вопрос, который задаёт AI в начале сессии — и который задаёт себе человек каждое утро:
«Текущая фаза» → что сейчас делается и насколько готово?
«Ограничения» → что нельзя трогать и почему?
«Что сделано» → где граница между готовым и нет?
«Следующее действие» → с чего начать прямо сейчас?
«Известные проблемы» → о чём знаем, но не решаем сейчас?
Дата обновления — первая строка, всегда. Устаревший WAL хуже отсутствующего: создаёт ложную уверенность. Дата позволяет обнаружить это мгновенно.
Раздел «Ограничения» — самый важный. AI оптимизирует локально: текущую функцию,
текущий модуль. Без записи match_by_hash(): НЕ ТРОГАТЬ агент увидит функцию
и «улучшит» её. С записью — прочитает причину и обойдёт. Каждое ограничение
в WAL — это предотвращённый регресс.
Живой пример:
# WAL — Project Continuation State
_Обновлён: 2026-03-05T18:23:00Z_
## Текущая фаза
PROP-003 verification engine — IN PROGRESS
## Ограничения (не нарушать без обсуждения)
- Таймаут: 600s (spec://oproto/PROP-003#verification.timeout)
- match_by_hash(): НЕ ТРОГАТЬ (issue #12, хрупкая reconnection-логика)
- Хэширование: blake3, не SHA-256 (spec://common/PROP-000#hash)
## Что сделано
- [x] PROP-001 OPROTO base — complete
- [x] PROP-002 Edge server skeleton — complete, 47/52 тестов
- [ ] PROP-003 Verification engine — в процессе
- Написан: matcher по sender_id + blake3 hash
- Не написано: degraded mode, timeout logic, reconciliation
## Следующее действие
src/verify.rs → degraded_mode_handler()
Начни с: cargo test --package oproto-verify
## Известные проблемы
- grammers reconnection после потери сети (#12)
- protobuf schema для media_refs (#15)
Иногда WAL накапливает детали, которые перестали быть срочными, но ещё не убраны.
Перед началом сессии полезно запросить краткое резюме. Добавь в CLAUDE.md:
## /wal-status
Прочитай specs/WAL.md полностью. Дай краткий статус:
- Одна строка: текущая фаза и её статус
- До трёх пунктов: что требует внимания (блокеры, риски)
- Одна строка: следующий приоритетный шаг
Формат: не более 10 строк.
Если WAL не обновлялся более 24 часов — предупреди первой строкой.
Результат — ориентировка за 30 секунд:
⚠️ WAL обновлён 26 часов назад — возможно устарел.
Фаза: PROP-003 verification engine, ~70% готово
Требует внимания:
• reconcile_pending() — stub, заблокирован issue #12
• protobuf schema для media_refs (#15) — нужно до PROP-004
Следующий шаг: решить #12 или начать PROP-004 параллельно.
Перед началом сессии Олег замечает: в WAL написано «Таймаут: 600s», но почему 600s — не записано. Вчера он измерил: при 300s около 15% пользователей на корпоративных VPN получали false positives. Это важное знание, которое исчезнет через неделю.
Он дописывает в PROP-003:
### Таймаут верификации {#verification.timeout}
**Решение:** 600 секунд
**Почему:** При тестировании обнаружили: ~15% пользователей на корпоративных
VPN имеют задержку доставки >300s. Сообщения помечались TIMEOUT до того, как
Telegram успевал подтвердить. Измерено на logs/vpn-test-2026-03-05.log,
847 сообщений, 128 пользователей.
**Что рассматривали:**
- Адаптивный таймаут по latency: непредсказуемый UX — отброшен
- 300s с retry: добавляет complexity, не решает корень — отброшен
**Когда пересмотреть:** если p99 latency сети упадёт ниже 100s
по данным мониторинга.
Разница принципиальная:
Было: «Таймаут: 600s»
Стало: «600s, потому что VPN false positives, измерено, вот данные»
Факт восстанавливается из кода за секунду. Решение нельзя восстановить вообще. AI через месяц увидит 600s, решит что это произвольное число, и предложит 300s «для производительности». Записанное решение — иммунитет от повторного обсуждения.
Последний пункт шаблона критичен: «когда пересмотреть». Решения без условий пересмотра превращаются в священных коров. Явное условие удерживает решение живым.
Есть разница между работой с AI в чате и работой с агентом.
В чате есть диалог. Написал расплывчато — AI переспросит. Ошибся — исправишь в следующем сообщении.
В агентском режиме — Claude Code, Kiro, Codex — диалога нет. Агент читает BOOT.md, WAL, спеки — и работает автономно. Часами. Переспросить некого.
Вот два WAL — один написан для чата, другой для агента:
# WAL для чата (сойдёт)
Работаем над верификацией, есть вопросы по таймаутам.
# WAL для агента (исполняется буквально)
## ОГРАНИЧЕНИЯ — НЕ НАРУШАТЬ
- Таймаут верификации: 600s, не 300s → spec://oproto/PROP-003#verification.timeout
- match_by_hash(): НЕ ТРОГАТЬ → issue #12, хрупкая reconnection-логика
- Хэширование: blake3, не SHA-256 → нет зависимости от OpenSSL
## Текущая задача
src/verify.rs → degraded_mode_handler()
Запуск: cargo test --package oproto-verify
Критерий готовности: все тесты зелёные
Второй WAL — не бюрократия. Это разница между агентом, который тихо меняет таймаут на «оптимальный» 300s (ты потеряешь 15% VPN-пользователей), и агентом, который делает именно то, что нужно.
Правило: чем больше автономии у агента, тем точнее должен быть WAL. Для агента,
который делает один шаг и ждёт подтверждения, достаточно грубого контекста. Для агента,
который работает несколько часов — каждое ограничение должно быть явным и адресованным
через spec://.
С этим пониманием смотрим, как выглядит реальный рабочий день.
Посмотрим как иерархия памяти выглядит в живом проекте. Один рабочий день: Олег реализует degraded mode для верификации сообщений. Edge-сервер должен работать, когда Telegram недоступен. Это FEAT-007.
8:47. Первое действие — не «открыть IDE». Первое действие — прочитать WAL:
cat specs/WAL.md
# или:
# /wal-status ← если WAL вырос
Дата обновления — вчерашний вечер. Актуально. Олег понимает, где он находится, за 30 секунд.
9:15. Олег открывает Claude Code:
Прочитай specs/BOOT.md, затем /wal-status, затем specs/modules/oproto/PROP-003.md.
Задача: реализовать degraded_mode_handler() в src/verify.rs.
После завершения — обнови WAL и закоммить.
Через 40 минут Олег смотрит не на код — на diff спецификации. AI добавил в PROP-003:
### Degraded mode handler {#degraded.handler}
**Реализовано в:** src/verify.rs::degraded_mode_handler()
**Тесты:** tests/oproto/degraded_mode.rs (3 теста)
**REVIEW:** reconcile_pending() зависит от reconnection logic (issue #12).
Текущая реализация: best-effort. Требует решения #12 для production.
AI сам пометил риск. Это ожидаемо — об этом было в WAL.
Когда Олег говорит «закоммить» — AI не просто делает git commit. AI пишет commit
message из контекста сессии. В момент коммита у AI есть полная история: все принятые
решения, все отброшенные варианты, все обнаруженные риски. У Git hook этой информации нет.
В CLAUDE.md можно прописать протокол:
## Протокол коммита
При команде «закоммить»:
1. Напиши commit message:
- Строка 1: тип(модуль): описание [трек-ID]
- Блок «Решения»: что выбрано и почему
- Блок «Отброшено»: альтернативы и причины
- Блок «Ограничения»: что осталось нерешённым
2. Покажи черновик для подтверждения
3. Только после подтверждения — git commit
4. Не пушить без явной команды «push»
Результат:
feat(oproto): degraded mode verification handler [FEAT-007]
Решения:
- 600s timeout (не 300s): false positives на VPN при 300s, измерено
на 847 сообщениях (logs/vpn-test-2026-03-05.log)
- match_by_hash() не тронут: хрупкая reconnection логика, issue #12
Отброшено:
- Адаптивный таймаут: непредсказуемый UX
- Оптимистичная верификация: неприемлемый security trade-off
Ограничения:
- reconcile_pending() — stub до решения issue #12
Через полгода кто-то откроет git log и прочитает намерение, а не просто изменения.
git log превращается в историю архитектурных решений.
Milestone commit фиксирует итог сессии. Но иногда этого недостаточно: нужно знать не только что решили, но как именно код был написан — дословно, с полным ходом рассуждений. Для этого существуют snaps — снимки сессий, привязанные к коммитам. О них — в следующей главе.
Правило нисходящей записи — основной путь. Но есть два сценария, где изменение происходит раньше, чем человек успевает обновить спеку.
Сценарий А: ты правишь код руками — потому что быстрее написать пять строк в редакторе, чем объяснять намерение в спеке. Это нормально и это часто правильное решение. Плохо только одно: теперь спека врёт. Следующая сессия AI прочитает спеку и «исправит» твой код обратно — и будет права по букве протокола.
Сценарий Б: ты даёшь команду в чате — «измени таймаут на 600s», «добавь логирование в этот метод». Агент выполняет, код меняется. Спеку никто не обновлял.
В обоих случаях нужен один шаг — сразу после изменения кода:
Посмотри git diff HEAD.
Пойми, что изменилось и почему.
Обнови соответствующую спеку так, чтобы она соответствовала коду.
Покажи черновик изменений — не применяй до подтверждения.
AI читает diff, формулирует намерение, предлагает изменение спеки:
# PROP-003§verification.timeout
- Unverified messages older than 300 seconds get status TIMEOUT.
+ Unverified messages older than 600 seconds get status TIMEOUT.
+ Rationale: 300s caused false positives for VPN users (issue #47).
Ты читаешь одну строчку. Если AI понял правильно — подтверждаешь. Спека и код снова синхронизированы. Весь протокол занимает тридцать секунд.
Это «императивный» режим разработки: ты говоришь что сделать, не обновляя спеку заранее. Sync-from-Code — это инструмент, который позволяет работать в этом режиме, не накапливая дрейф между кодом и спекой.
Когда не использовать. Если изменение в коде — временный хак, который ты
собираешься убрать через час, — не надо синхронизировать спеку под него. Но тогда
запиши в WAL явно: src/verify.rs: временный хак для отладки, не синхронизировать.
18:30. Одно действие в конце дня.
# WAL — Project Continuation State
_Обновлён: 2026-03-06T18:34:00Z_
## Текущая фаза
PROP-003 verification engine — ПОЧТИ ГОТОВО
## Ограничения (не нарушать без обсуждения)
- Таймаут: 600s (spec://oproto/PROP-003#verification.timeout)
- match_by_hash(): НЕ ТРОГАТЬ (issue #12)
- Хэширование: blake3, не SHA-256
## Что сделано
- [x] PROP-001, PROP-002 — complete
- [x] PROP-003 degraded_mode_handler() — COMPLETE, 3 теста зелёных
- reconcile_pending() — stub, ждёт #12
## Следующее действие
Решить #12 (reconnection strategy) — тогда дописать reconcile_pending().
Альтернатива: начать PROP-004 (Tauri UI) параллельно.
## Известные проблемы
- grammers reconnection (#12) — блокирует reconciliation
- protobuf schema для media_refs (#15) — нужно до PROP-004
Обрати внимание: WAL не говорит «деградед мод реализован вот так». Детали реализации — в спеке и в git-истории. WAL — это компас, не карта.
Это — полная архитектура памяти для одного разработчика с одним агентом. Она работает. Но как только в системе появляется второй участник — человек или ещё один агент — ситуация меняется принципиально. Об этом — следующая глава.
Мы описали что записывать и где хранить для индивидуальной работы. Но milestone commit фиксирует только итог сессии. Что если через три месяца нужно понять, как именно было написано то или иное решение? И что происходит, когда WAL перестаёт работать — потому что появился второй разработчик или второй агент?
Следующая глава — «Масштабирование памяти»: snaps как глубокая история сессий, эпики как единица командной работы, и что делать, когда параллельных агентов становится больше одного.
Про attention degradation: Nelson F. Liu et al., «Lost in the Middle: How Language Models Use Long Contexts» (TACL, 2024).
Про Write-Ahead Log и checkpoints: Martin Kleppmann, Designing Data-Intensive Applications (O'Reilly, 2017), глава 3.
Про distributed cognition: Edwin Hutchins, Cognition in the Wild (MIT Press, 1995).
Про decision logs: Addy Osmani, «Automated Decision Logs in AI-Assisted Coding» (ноябрь 2024).