Andrey Epifancev c711a119b9 eva-agent v2: версии в e2e, документация, решения плана (#7)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:59:56 +04:00

7.7 KiB
Raw Blame History

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. cmf_author по имени поля раскрывается в объект с name и login (как у CmfDocument).
  • Черновик (has_published = false) Eva меняет на месте.

Goals / Non-Goals

Goals: опубликованные версии страниц дерева — хабу, без повторной перекачки известных; работа с хабом без истории.

Non-Goals: см. proposal.md.

Decisions

  1. Шаг версий — после страниц, в той же команде. Страницы отправлены — затем версии; отчёт один. Порядок страниц — порядок обхода.
  2. Выборка по странице дерева. Для каждой страницы обхода — 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 по этому полю не проверялся.
  3. Текст — только новых. Версия, чей код есть в known_versions[<код страницы>], пропускается без запроса текста. Для остальных — CmfDocumentHistory.get по коду с полями ["code", "text"]; html = text (не строка — пустая строка); attachments, links — classify_links(html, base), как у страниц.
  4. Автор. {login, name} из cmf_author; объекта нет или поля закрыты (_acl_fields) — {login: null, name: null}.
  5. Пачки. Те же пределы, что у страниц: ≤50 версий и ≤4 МБ JSON на пачку; 413 от хаба — ошибка с кодом версии или границами пачки (как _send_pages).
  6. Совместимость с прежним хабом. Ключа known_versions в параметрах нет — шаг версий не выполняется, в отчёте нет versions. Пустой объект {} — первый прогон, вся история.
  7. Ошибки. Ошибка Eva на шаге версий (EvaError, сеть после повторов) — команда failed с текстом, как на шаге страниц; хаб оставит копию и историю прежними (решение 10 change хаба). Версия без code или без cmf_created_at пропускается с записью в лог.
  8. Счётчики. result.versions — число отправленных версий; eva_calls включает вызовы шага версий.
  9. Тесты. 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 шаг версий проверяется только юнит-тестами.