9.4 KiB
9.4 KiB
Context
do_wiki_sync обходит дерево от корня (build_children + walk), читает текст каждой страницы
(CmfDocument.get … ["code", "name", "text"]) и шлёт пачки страниц POST /agent/wiki/pages (≤50 страниц, ≤4 МБ JSON),
затем отчитывается done {root_title, codes, eva_calls}. Хаб с change wiki-history дописывает в параметры выдачи
known_versions и принимает версии отдельным маршрутом.
Проверено на живой Eva 28.10.2026:
CmfDocumentHistory.listсfilter=[["parent_id", "==", "CmfDocument:<uuid>"]]работает;parent.code— ошибка-32001(вложенный фильтр).- Поля версии:
code(DOCH-…),cmf_created_at(ISO со смещением+03:00),cmf_author_id,text(полный HTML, до ~56 тыс. символов),has_published,cur_workflow. УCmfDocumentполеcmf_authorпо имени раскрывается в объект сnameиlogin; уCmfDocumentHistoryпроба проверяла толькоfields: ["**"]— раскрытие по имени поля не проверено (I-1). - Черновик (
has_published = false) Eva меняет на месте.
Goals / Non-Goals
Goals: опубликованные версии страниц дерева — хабу, без повторной перекачки известных; работа с хабом без истории.
Non-Goals: см. proposal.md.
Decisions
- Шаг версий — после страниц, в той же команде. Страницы отправлены — затем версии; отчёт один. Порядок страниц — порядок обхода.
- Выборка по странице дерева. Для каждой страницы обхода —
CmfDocumentHistory.listс фильтромparent_id= id страницы из обхода (doc["id"]), полями["code", "cmf_created_at", "has_published", "cmf_author"], страницами поPAGE_SIZEс той же защитой от несоблюденияslice, что уlist_docs(общий помощник для класса). Опубликованные отбираются у себя (has_published is True): фильтр Eva по этому полю не проверялся. - Текст — только новых. Версия, чей код есть в
known_versions[<код страницы>], пропускается без запроса текста. Для остальных —CmfDocumentHistory.getпо коду с полями["code", "text"];html=text(не строка — пустая строка);attachments,links—classify_links(html, base), как у страниц. - Автор.
{login, name}изcmf_author; объекта нет или поля закрыты (_acl_fields) —{login: null, name: null}. - Пачки. Те же пределы, что у страниц: ≤50 версий и ≤4 МБ JSON на пачку; 413 от хаба — ошибка с кодом версии или
границами пачки (как
_send_pages). - Совместимость с прежним хабом. Ключа
known_versionsв параметрах нет — шаг версий не выполняется, в отчёте нетversions. Пустой объект{}— первый прогон, вся история. - Ошибки. Ошибка Eva на шаге версий (
EvaError, сеть после повторов) — командаfailedс текстом, как на шаге страниц; хаб оставит копию и историю прежними (решение 10 change хаба). Версия безcodeили безcmf_created_atпропускается с записью в лог. - Счётчики.
result.versions— число отправленных версий;eva_callsвключает вызовы шага версий. - Тесты.
tests/fake_eva.pyполучаетCmfDocumentHistory.list(фильтрparent_id) и.get(поcode); сквозной прогон с тестовым хабом — первая загрузка и догрузка одной новой версии.
Risks / Trade-offs
- Один вызов Eva на страницу при каждой синхронизации; при пачке новых версий — ещё по вызову на версию. Первый прогон
AWPCA (28 страниц, десятки версий) — порядка сотни вызовов с паузой
wiki_pacing. - Если Eva однажды поменяет текст опубликованной версии, агент её не перечитает (код уже известен хабу).
Decisions made without discussion
- 28.10.2026 — отбор опубликованных у себя, не фильтром Eva (решение 2). Обоснование: фильтр по
has_publishedне проверялся, а лишние метаданные дёшевы. Цена ошибки: чуть больше данных в ответе на страницу. - 28.10.2026 — пустой автор
{null, null}вместо отказа (решение 4). Цена ошибки: в ленте правка без имени. - 28.10.2026 — без
known_versionsшаг пропускается (решение 6). Цена ошибки: нет — прежний хаб маршрута версий не знает. - 28.10.2026 — P-1. Автор — по полям.
login/nameизcmf_authorберутся по отдельности: строка илиnull(закрытое ACL поле Eva не отдаёт —_acl_fields: deny);cmf_authorне объект —{null, null}. Уточняет решение 4. Цена ошибки: в ленте правка без имени или без логина. - 28.10.2026 — P-2. Страница обхода без
id— версии не запрашиваются, строка в лог, команда продолжается. Цена ошибки: у такой страницы нет истории. - 28.10.2026 — P-3. Битый
known_versions(не объект или значение не список строк) —failed«wiki_sync: known_versions — не объект {код страницы: [коды версий]}» до вызовов Eva. Обоснование: иначе хаб получил бы всю историю заново. Цена ошибки: синхронизация стоит до исправления хаба. - 28.10.2026 — P-4. Версия исчезла между
listиget—failed«версия <код> страницы <код> не найдена или нет прав», как пропавшая страница. Цена ошибки: редкая гонка с правкой в Eva останавливает синхронизацию до повтора. - 28.10.2026 — P-5. Порядок версий — порядок обхода страниц, внутри страницы — порядок ответа Eva; хаб сортирует сам. Цена ошибки: нет.
- 28.10.2026 — P-6. e2e с историей — по признаку хаба (
history_atвGET /wiki/status); с хабом без истории блок пропускается с сообщением. Цена ошибки: до merge hub#140 шаг версий проверяется только юнит-тестами. - 28.10.2026 — I-1 (финальное ревью). Версии без автора — строка в лог «N из M версий без автора (cmf_author не
раскрыт или закрыт ACL)» на синхронизацию. Обоснование: автор известной версии не перечитывается, потеря необратима.
Перед подъёмом
wiki_min_agent_version— пробаCmfDocumentHistory.listсfields=["cmf_author"]на виртуалке. Цена ошибки: нет. - 28.10.2026 — M-1 (финальное ревью, поднято до Important). Код версии уходит один раз за прогон, даже если Eva
при сдвиге
sliceотдала запись повторно. Обоснование: хаб считает уникальные коды, повтор давалfailed«нет версий: пришло N−1 из N». Цена ошибки: нет. - 28.10.2026 — M-3 (финальное ревью, поднято до Important). Нет ключа
textв ответеCmfDocumentHistory.get—failed«версия X страницы Y: Eva не отдала text (нет прав?)». Уточняет решение 3: пустая версия в хабе не исправилась бы (код уже известен).text: nullпо-прежнему — пустая строка. Цена ошибки: при закрытом ACLtextсинхронизация стоит до выдачи прав.