103 lines
10 KiB
Markdown
103 lines
10 KiB
Markdown
## 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`
|