eva-agent MVP: wiki_sync и wiki_fetch (#1) #2

Merged
aep merged 15 commits from feat/1-mvp into main 2026-10-24 09:05:15 +03:00
Owner

Closes #1

MVP агента модуля wiki хаба. Контракт с хабом — jira-hub openspec/changes/wiki-module/ (PR jira-hub#92), решения агента — openspec/changes/eva-agent-mvp/design.md (#1–#45).

Что сделано

  • src/agent.py (Python 3.9, stdlib, AGENT_VERSION = 1):
    • общие части jira-agent v14 без изменения смысла: HTTP, транспорт через общую папку, HubClient, журнал, цикл, самообновление, CLI run [--once] | check | selftest | prune;
    • EvaClient — JSON-RPC 2.2, повторы на 429 (Retry-After), 502/503/504 и сетевые сбои, пауза wiki_pacing, страницы списка по 200;
    • classify_links — вложения /files/… (миниатюра .meta/thumbnail.jpg → файл), ссылки project/Document/DOC-…, чужой хост и static/ отбрасываются;
    • wiki_sync — дерево по parent/tree_parent проекта Eva (запасной путь — tree_parent.code), обход в глубину с защитой от циклов, пачки по 50 страниц / 4 МБ, отчёт {root_title, codes, eva_calls};
    • wiki_fetch — только хост Eva, поток до 10 МБ, POST /agent/files.
  • src/run.py — дословная копия запускателя jira-agent (проверяет test_run_py_is_copy).
  • README: установка на виртуалку, launchd, выкатка через jira-hub agent publish --module wiki.
  • scripts/e2e-hub.sh — сквозной прогон с тестовым хабом из ветки jira-hub#92 (временный JIRA_HUB_HOME, фейковая Eva).

Проверка

  • /usr/bin/python3 -W error::ResourceWarning -m unittest discover -s tests -t . — Ran 238 tests … OK.
  • bash scripts/e2e-hub.sh — e2e: ok. Хаб принял 5 страниц, Markdown содержит attachment:, wiki_fetch прошёл, sha256 файла совпал.

Проверить на первой живой синхронизации

Тестами этого не проверить:

  • принимает ли Eva Bearer на /files/… и отдаёт ли файл без ?version=1 (решение 7);
  • нет ли SSO-редиректа на файлах (M-5);
  • соблюдает ли Eva slice (M-4).

Rulings I made

  • R-12 (#12). ref — значение атрибута после unescape: хаб (convert.py, bs4) ищет по тому же значению.
    Цена ошибки: адреса с & не сопоставятся, правка одной строки.
  • R-13 (#13). При расхождении плана с кодом хаба по протоколу прав код хаба.
    Цена ошибки: агент подгонится под ошибку хаба, видно в e2e.
  • R-14 (#44). Финальная волна закрыла I-1…I-3, M-1…M-4, M-6, M-7, T5.2, T6.1, T6.4. M-5 отложен, чтобы не отвергать настоящие .html-вложения.
    Цена ошибки: страница входа SSO может попасть во вложение.
  • R-15 (#45). T2.3 и T5.1 отклонены по коду.
    Цена ошибки: нет.
  • Решения исполнителей #14–#43 (design.md). Главные:
    • EvaClient(cfg) (#20);
    • обход по коду с картой id→код (#26, #31);
    • страница без названия получает title = code (#27);
    • исчезнувшая страница даёт failed (#28);
    • run --once (#34);
    • ForeignHost и кодирование URL (#37).
  • Отклонение от плана: путь к jira-agent для test_run_py_is_copy ищется на 1���3 уровня выше (#18).

Отложено (в follow-up при закрытии)

  • T1.1 — нет теста паузы на 403;
  • T2.1 — сигнатура EvaClient.call;
  • T2.2 — тест скраба токена в ConnectionError;
  • T3.1 — DOC-код без якоря сегмента;
  • T3.2 — регистр хоста и порт по умолчанию;
  • T3.3 — незакрытый <a>;
  • T6.2 — HUB_SRC в e2e;
  • T6.3 — токены e2e в argv curl;
  • M-5 — SSO-редирект на файлах;
  • O-3 — устаревший абзац CLAUDE.md.

Отклонение от процесса

main репозитория создан агентом из bootstrap-коммита обвязки (f944262) прямым push: adopt-project.sh не умеет работать с пустым репозиторием и не создал main.

🤖 Generated with Claude Code

Closes #1 MVP агента модуля `wiki` хаба. Контракт с хабом — jira-hub `openspec/changes/wiki-module/` (PR jira-hub#92), решения агента — `openspec/changes/eva-agent-mvp/design.md` (#1–#45). ## Что сделано - **`src/agent.py`** (Python 3.9, stdlib, `AGENT_VERSION = 1`): - общие части jira-agent v14 без изменения смысла: HTTP, транспорт через общую папку, `HubClient`, журнал, цикл, самообновление, CLI `run [--once] | check | selftest | prune`; - `EvaClient` — JSON-RPC 2.2, повторы на 429 (`Retry-After`), 502/503/504 и сетевые сбои, пауза `wiki_pacing`, страницы списка по 200; - `classify_links` — вложения `/files/…` (миниатюра `.meta/thumbnail.jpg` → файл), ссылки `project/Document/DOC-…`, чужой хост и `static/` отбрасываются; - `wiki_sync` — дерево по `parent`/`tree_parent` проекта Eva (запасной путь — `tree_parent.code`), обход в глубину с защитой от циклов, пачки по 50 страниц / 4 МБ, отчёт `{root_title, codes, eva_calls}`; - `wiki_fetch` — только хост Eva, поток до 10 МБ, `POST /agent/files`. - **`src/run.py`** — дословная копия запускателя jira-agent (проверяет `test_run_py_is_copy`). - **README:** установка на виртуалку, launchd, выкатка через `jira-hub agent publish --module wiki`. - **`scripts/e2e-hub.sh`** — сквозной прогон с тестовым хабом из ветки jira-hub#92 (временный `JIRA_HUB_HOME`, фейковая Eva). ## Проверка - `/usr/bin/python3 -W error::ResourceWarning -m unittest discover -s tests -t .` — `Ran 238 tests … OK`. - `bash scripts/e2e-hub.sh` — `e2e: ok`. Хаб принял 5 страниц, Markdown содержит `attachment:`, `wiki_fetch` прошёл, sha256 файла совпал. ## Проверить на первой живой синхронизации Тестами этого не проверить: - принимает ли Eva Bearer на `/files/…` и отдаёт ли файл без `?version=1` (решение 7); - нет ли SSO-редиректа на файлах (M-5); - соблюдает ли Eva `slice` (M-4). ## Rulings I made - **R-12 (#12).** `ref` — значение атрибута после unescape: хаб (`convert.py`, bs4) ищет по тому же значению. Цена ошибки: адреса с `&amp;` не сопоставятся, правка одной строки. - **R-13 (#13).** При расхождении плана с кодом хаба по протоколу прав код хаба. Цена ошибки: агент подгонится под ошибку хаба, видно в e2e. - **R-14 (#44).** Финальная волна закрыла I-1…I-3, M-1…M-4, M-6, M-7, T5.2, T6.1, T6.4. M-5 отложен, чтобы не отвергать настоящие `.html`-вложения. Цена ошибки: страница входа SSO может попасть во вложение. - **R-15 (#45).** T2.3 и T5.1 отклонены по коду. Цена ошибки: нет. - **Решения исполнителей** #14–#43 (design.md). Главные: - `EvaClient(cfg)` (#20); - обход по коду с картой id→код (#26, #31); - страница без названия получает `title = code` (#27); - исчезнувшая страница даёт failed (#28); - `run --once` (#34); - `ForeignHost` и кодирование URL (#37). - **Отклонение от плана:** путь к jira-agent для `test_run_py_is_copy` ищется на 1���3 уровня выше (#18). ## Отложено (в follow-up при закрытии) - T1.1 — нет теста паузы на 403; - T2.1 — сигнатура `EvaClient.call`; - T2.2 — тест скраба токена в `ConnectionError`; - T3.1 — DOC-код без якоря сегмента; - T3.2 — регистр хоста и порт по умолчанию; - T3.3 — незакрытый `<a>`; - T6.2 — `HUB_SRC` в e2e; - T6.3 — токены e2e в argv curl; - M-5 — SSO-редирект на файлах; - O-3 — устаревший абзац CLAUDE.md. ## Отклонение от процесса `main` репозитория создан агентом из bootstrap-коммита обвязки (f944262) прямым push: `adopt-project.sh` не умеет работать с пустым репозиторием и не создал `main`. 🤖 Generated with [Claude Code](https://claude.com/claude-code)
aep added 15 commits 2026-10-24 05:10:47 +03:00
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
cmd_check проверяет хаб и Eva (код 0 или 3), флаг run --once,
scripts/e2e-hub.sh поднимает тестовый хаб во временном JIRA_HUB_HOME.
Решения 33–35 в design.md.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
На Python 3.9 `<![foo]>` вызывал NotImplementedError из ParserBase.error(), а адрес вида `http://[::1` — ValueError в
urljoin/urlsplit; одна такая конструкция роняла wiki_sync целиком. Теперь нестандартная секция пропускается до `>`,
битая ссылка — пропускается, остальные ссылки страницы сохраняются.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Адрес вложения из HTML бывает с пробелами и кириллицей: путь и запрос теперь кодируются перед запросом, готовые %XX
не кодируются повторно, одиночный % становится %25. Отказ по хосту — ForeignHost («чужой хост» или «некорректный
адрес»), а не голый ValueError: UnicodeEncodeError и прочие ValueError больше не маскируются под «чужой хост».
Добавлены тесты на похожий хост, userinfo, другой порт и схему.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- EvaError (Eva вернула `error`) — failed «Eva: <текст>» без повтора, а не «внутренняя ошибка агента».
- 413 хаба на пачку страниц — в ошибке названа страница («страница DOC-x больше 16 МБ») или границы пачки.
- list_docs останавливается, если Eva не соблюдает slice и ответ не приносит новых записей.
- Удалены неиспользуемые Executor.s и Executor.sleep; Executor(eva, hub).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- CLAUDE.md: `jira-hub agent publish --module wiki src/agent.py`, порог `wiki_min_agent_version`.
- README: `wiki_min_agent_version` вместо `min_agent_version`; `eva.base_url` — только origin; агент только для macOS
  (fcntl), пример launchd `ru.aepif.eva-agent`; строка про проверку SSO на первой живой синхронизации (M-5).
- ARCHITECTURE: строки Eva и Executor по реализованному, строки ссылок (`_LinkParser`, `classify_links`) и дерева
  (`build_children`, `walk`).
- design.md: решения 36–43, в том числе почему M-5 отложен до живой проверки.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
aep merged commit e5327011ae into main 2026-10-24 09:05:15 +03:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: aep/eva-agent#2
No description provided.