Andrey Epifancev 5c487d0ae9 OpenSpec: design-only change wiki-versions — версии страниц в wiki_sync (#7)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:31:32 +04:00

5.6 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). Цена ошибки: нет — прежний хаб маршрута версий не знает.