Andrey Epifancev 98842208f5 OpenSpec: change wiki-history — история правок вики, лента и дифф (#140)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-28 11:31:32 +04:00

10 KiB
Raw Blame History

ADDED Requirements

Requirement: История правок страниц

Хаб SHALL хранить опубликованные версии страниц дерева проекта: код версии Eva (DOCH-…), код страницы, время правки (UTC), автора (login, name), Markdown версии и число строк, добавленных и удалённых против предыдущей версии той же страницы (порядок версий — по времени, затем по коду; у первой — против пустого текста). Время правки — время создания версии в Eva (cmf_created_at). Версии страниц, которых нет в дереве проекта, хаб MUST NOT хранить; неопубликованные версии (черновики) агент MUST NOT присылать.

При выдаче wiki_sync хаб SHALL дописывать в параметры команды known_versions — объект {<код страницы>: [<код версии>…]} по версиям, уже хранящимся у страниц текущей копии проекта (без копии — {}); в command.params в базе поле MUST NOT записываться. Агент wiki SHALL после страниц прислать опубликованные версии страниц дерева, которых нет в known_versions их страницы, пачками POST /agent/wiki/versions {command_id, versions}: версия — {page_code, code, created_at, author: {login, name}, html, attachments, links} (created_at — ISO-8601 со смещением; login и name — строки или null, если Eva автора не отдала; attachments и links — как у страниц, по html версии), в пачке не больше 50 версий, — и отчитаться done с result.versions — числом присланных версий. Пачка по команде не в claimed или не wiki_sync — 409; неверная форма версии — 422 bad_versions с кодом версии и полем. Хаб SHALL переводить HTML версии в Markdown при приёме вне транзакции записи (ссылки на вложения — текстом 📎 <имя> без ссылки, ссылки на страницы — page:<код>) и складывать в стадию команды.

По отчёту done с result.versions хаб SHALL в той же операции, что и копию (см. «Синхронизация вики»): добавить версии из стадии к страницам, чьи коды есть в codes, отбросив остальные; удалить версии страниц, которых нет в codes; посчитать «+/−» у новых версий и у версии, следующей за вставленной раньше неё; записать время применения истории history_at. Присланных версий меньше result.versions — команда failed «нет версий: пришло N из M», копия и история прежние. Отчёт done без result.versions (агент без истории) SHALL обновлять копию как прежде и не менять историю и history_at. Отчёт failed и отброшенный отчёт по сменённому корню историю не меняют. Сброс корня SHALL удалять историю проекта вместе с копией. Стадия версий очищается так же, как стадия страниц.

Scenario: Первая загрузка истории

  • WHEN у AWPCA дерево DOC-1, DOC-2; в хабе версий нет; выдан wiki_sync
  • THEN в параметрах выдачи known_versions = {}; агент прислал 3 версии DOC-1 и 2 версии DOC-2, отчёт done {versions: 5}; в хабе 5 версий, у первой версии каждой страницы «+N / −0», history_at заполнен

Scenario: Догрузка новых версий

  • WHEN у DOC-1 в хабе версии DOCH-10, DOCH-11; в Eva опубликована DOCH-12
  • THEN при выдаче known_versions = {"DOC-1": ["DOCH-10", "DOCH-11"], …}; агент прислал только DOCH-12; у неё «+/−» против DOCH-11

Scenario: Агент без истории

  • WHEN агент v1 отчитался done {root_title, codes, eva_calls} без versions
  • THEN копия обновлена, версии и history_at прежние

Scenario: Страница ушла из дерева

  • WHEN у DOC-3 в хабе 4 версии; новая синхронизация вернула codes без DOC-3
  • THEN версий DOC-3 в хабе нет

Scenario: Версия чужой страницы

  • WHEN в стадии версия с page_code = DOC-99, которой нет в codes
  • THEN версия не записана, команда done

Scenario: Потерянная пачка версий

  • WHEN отчёт done {versions: 5}, в стадии 3 версии
  • THEN команда failed «нет версий: пришло 3 из 5», копия и история прежние

Requirement: Маршруты истории вики

Хаб SHALL предоставлять под клиентским токеном (проект — project=<метка>, неизвестный — 404, без корня — 409 no_root), читая только базу хаба:

  • GET /wiki/changes?project=&since=&until=&author=&limit= — правки страниц текущей копии проекта со временем в [since, until]. since, until — ISO-8601 со смещением или Z (без смещения — 422); без until — текущее время; без since — until минус 24 часа; author — подстрока имени или логина без учёта регистра; limit — по умолчанию 500, 1–2000. Ответ — {since, until, synced_at, history_at, truncated, changes: [{at, page_code, title, path, version, author: {login, name}, added, removed}]} по at по убыванию, затем по version по убыванию; правок больше limit — первые limit и truncated = true. Неверное время, since позже until, limit вне 1..2000 — 422 bad_request с именем параметра. history_at = null — история проекта ещё не загружалась.
  • GET /wiki/pages/{code}/versions?project= — {code, title, versions: [{version, at, author: {login, name}, added, removed}]}, новые первыми; страницы нет в копии — 404.
  • GET /wiki/pages/{code}/diff?project=&version=&offset= — правка version (без параметра — последняя) против предыдущей версии страницы: {code, title, path, version, prev_version, at, author: {login, name}, added, removed, index, total, offset, next_offset, diff}, где diff — unified diff Markdown (контекст 3 строки; у первой версии — против пустого, prev_version = null) с символа offset до 20 000 символов, next_offset — начало следующей части или null, index — номер версии от старой (с 1), total — число версий. Страницы нет в копии — 404; версии нет у страницы — 404 no_version; у страницы нет версий — 404 no_versions.

GET /wiki/pages/{code} SHALL дополнительно отдавать modified_at, modified_by: {login, name} (последняя версия; нет версий — null) и versions — число версий. GET /wiki/status SHALL дополнительно отдавать history_at.

Scenario: Что сделали за сегодня

  • WHEN у AWPCA 27.10 в 17:59 и 18:02 (MSK) Соснов опубликовал версии DOC-101252, 26.10 Молчанов — версию DOC-2; GET /wiki/changes?project=AWPCA&since=2026-10-27T00:00:00+03:00
  • THEN две правки DOC-101252, новая первой, у каждой автор {login: "sosnov-dvi@rshbeco.ru", name: "Дмитрий Соснов"}, added/removed, путь страницы в дереве; правки DOC-2 нет

Scenario: Фильтр по автору

  • WHEN GET /wiki/changes?project=AWPCA&since=…&author=соснов
  • THEN только правки Соснова

Scenario: Дифф последней правки

  • WHEN у DOC-101252 4 версии; GET /wiki/pages/DOC-101252/diff?project=AWPCA
  • THEN index = 4, total = 4, prev_version — третья версия, diff начинается с --- и +++

Scenario: Длинный дифф

  • WHEN дифф правки 45 000 символов, запрос без offset
  • THEN первые 20 000 символов, next_offset = 20000; запрос с offset=40000 — остаток, next_offset = null

Scenario: История ещё не загружена

  • WHEN у AWPCA копия есть, агент v1, GET /wiki/changes?project=AWPCA
  • THEN 200, history_at = null, changes = []

Scenario: Последняя правка в странице

  • WHEN в хабе 4 версии DOC-101252, последняя — Соснов, created_at = 2026-10-27T23:48:02+03:00; GET /wiki/pages/DOC-101252?project=AWPCA
  • THEN modified_at = "2026-10-27T20:48:02+00:00", modified_by.name = "Дмитрий Соснов", versions = 4