Батч: бейдж контекста (#189) + e2e в CI (#187) + inline-тест MCP (#170) #197

Merged
vvzvlad merged 5 commits from batch/issues-189-187-170 into develop 2026-06-26 18:09:48 +03:00

Батч из трёх задач, каждая — отдельным коммитом.

#189 — feat(ai-chat): бейдж контекста «текущий / максимум» в шапке

Бейдж в шапке плавающего окна AI-чата больше не «прыгает» между live-счётчиком хода и размером контекста — теперь он всегда показывает текущий / максимум (например 572 / 200k). Максимум берётся из новой настройки AI «Context window (tokens)»: сервер резолвит её и кладёт maxContextTokens в метаданные завершённого ассистентского хода (рядом с contextTokens), поэтому клиенту не нужно резолвить модель, и это переживает публичные шары / пер-ролевые модели. Live-фидбек «Thinking · N tokens» в теле чата сохранён; из шапки убран только дублирующий live-счётчик. Подводный камень: провайдерные настройки хранятся как ::text, поэтому значение коэрсится обратно в положительное целое в resolve()/getMasked().
Коммит: feat(ai-chat): header badge shows current/max context, max from AI settings (#189)

#187 — ci(develop): e2e на каждый push в develop, без блокировки деплоя

В develop.yml добавлены два независимых job — e2e-server и e2e-mcp (pgvector + redis, миграции, для MCP — сборка, старт прод-сервера с REST + /collab, seed через /api/auth/setup). build остаётся needs: test и не зависит от e2e: падение e2e не блокирует публикацию образа :develop, а делает run красным и шлёт письмо GitHub автору пуша.
Коммит: ci(develop): run server + mcp e2e on every develop push without blocking deploy (#187)

#170 — feat(ai-chat): кнопка «Test» на каждой строке списка MCP-серверов

Inline-проверка подключения прямо из строки списка внешних MCP-серверов, без попапов. Строка вынесена в AiMcpServerRow со своим инстансом мутации (изоляция состояния — нет глобального мигания). Состояния: покой (Test), проверка (loading), успех (зелёная, OK · N по числу инструментов), ошибка (красная, Failed); тултип показывает список инструментов или текст ошибки. Результат сбрасывается при смене url/transport/headers. Бэкенд/сервис/мутация не менялись.
Коммит: feat(ai-chat): inline Test button per external MCP server row (#170)


Closes #189
Closes #187
Closes #170

🤖 Generated with Claude Code

Батч из трёх задач, каждая — отдельным коммитом. ### `#189` — feat(ai-chat): бейдж контекста «текущий / максимум» в шапке Бейдж в шапке плавающего окна AI-чата больше не «прыгает» между live-счётчиком хода и размером контекста — теперь он всегда показывает `текущий / максимум` (например `572 / 200k`). Максимум берётся из новой настройки AI «Context window (tokens)»: сервер резолвит её и кладёт `maxContextTokens` в метаданные завершённого ассистентского хода (рядом с `contextTokens`), поэтому клиенту не нужно резолвить модель, и это переживает публичные шары / пер-ролевые модели. Live-фидбек «Thinking · N tokens» в теле чата сохранён; из шапки убран только дублирующий live-счётчик. Подводный камень: провайдерные настройки хранятся как `::text`, поэтому значение коэрсится обратно в положительное целое в `resolve()`/`getMasked()`. Коммит: `feat(ai-chat): header badge shows current/max context, max from AI settings (#189)` ### `#187` — ci(develop): e2e на каждый push в develop, без блокировки деплоя В `develop.yml` добавлены два независимых job — `e2e-server` и `e2e-mcp` (pgvector + redis, миграции, для MCP — сборка, старт прод-сервера с REST + `/collab`, seed через `/api/auth/setup`). `build` остаётся `needs: test` и **не** зависит от e2e: падение e2e не блокирует публикацию образа `:develop`, а делает run красным и шлёт письмо GitHub автору пуша. Коммит: `ci(develop): run server + mcp e2e on every develop push without blocking deploy (#187)` ### `#170` — feat(ai-chat): кнопка «Test» на каждой строке списка MCP-серверов Inline-проверка подключения прямо из строки списка внешних MCP-серверов, без попапов. Строка вынесена в `AiMcpServerRow` со своим инстансом мутации (изоляция состояния — нет глобального мигания). Состояния: покой (`Test`), проверка (loading), успех (зелёная, `OK · N` по числу инструментов), ошибка (красная, `Failed`); тултип показывает список инструментов или текст ошибки. Результат сбрасывается при смене url/transport/headers. Бэкенд/сервис/мутация не менялись. Коммит: `feat(ai-chat): inline Test button per external MCP server row (#170)` --- Closes #189 Closes #187 Closes #170 🤖 Generated with [Claude Code](https://claude.com/claude-code)
Ghost added 3 commits 2026-06-25 22:41:36 +03:00
Add two independent jobs to develop.yml — e2e-server and e2e-mcp — that run on
each push to develop alongside test/build. `build` stays `needs: test` only, so
a failing e2e never blocks the :develop image build/publish; the red run plus
GitHub's email to the pusher is the notification.

- e2e-server: pgvector + redis services, migrations, apps/server test:e2e.
- e2e-mcp: build editor-ext/server/mcp, migrate, start the prod server
  (REST + /collab in one process), wait for /api/health, seed the admin via
  /api/auth/setup, then run @docmost/mcp test:e2e.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Add a per-row Test button to the external MCP servers list that shows the
connection result inline (no toasts). Extract the row into AiMcpServerRow so
each row owns its own useTestAiMcpServerMutation instance — independent loading
and result, no cross-row flicker.

States: idle (Test), pending (loading), success (green, "OK · N" with the tool
count), failure (red, "Failed"); a tooltip shows the tool list or the error.
The result resets when url/transport/headers change (the row is keyed by id, so
it does not remount). Backend, service and mutation are unchanged.

- ai-mcp-servers.tsx: AiMcpServerRow + Test button + reset effect + tooltip.
- i18n: add Failed / "OK · {{n}}" (en, ru) and ru Test / tool-list keys.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
The floating chat window's header badge flipped meaning — a live per-turn token
counter while streaming, the persisted context size at rest — so it "reset to 1"
on each prompt and conflated two different numbers. Replace it with a stable
"current / max" context badge (e.g. `572 / 200k`). The live "Thinking · N tokens"
inside the chat body stays; only the duplicate live counter is removed from the
header.

Max comes from a new admin setting "Context window (tokens)". The server resolves
it and attaches `maxContextTokens` to the completed assistant turn's metadata
(next to contextTokens), so the badge needs no client-side model resolution and
this survives public shares / per-role models.

Server:
- ai.types: chatContextWindow on AiProviderSettings + PROVIDER_SETTINGS_KEYS +
  ResolvedAiConfig + MaskedAiSettings.
- workspace.repo: chatContextWindow in AI_PROVIDER_SETTINGS_ALLOWED (parity).
- update-ai-settings.dto: @IsInt @Min(0) chatContextWindow.
- ai-settings.service: coerce the ::text-stored value to a positive int in
  resolve()/getMasked().
- ai-chat.service: flushAssistant writes metadata.maxContextTokens (>0); the
  completed turn passes resolved.chatContextWindow.

Client:
- ai-chat.types: maxContextTokens on the message-row metadata.
- ai-chat-window: read maxContextTokens; render "current [/ max]"; drop the
  liveTurnTokens state/branch and the onLiveTurnTokens prop; new tooltip.
- chat-thread: remove the live-turn-token throttle effect and plumbing.
- count-stream-tokens: drop the now-dead liveTurnTokens()/types; keep
  estimateTokens.
- settings: chatContextWindow on IAiSettings(+Update) + a NumberInput in the AI
  provider settings form.

i18n: add the badge/settings keys (en, ru); remove the two now-unused keys.
Tests: flushAssistant maxContextTokens, DTO validation, trim token tests.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
vvzvlad added the feature label 2026-06-26 00:31:34 +03:00
Ghost added 1 commit 2026-06-26 17:24:51 +03:00
Code-review follow-ups (Approve-with-comments) for batch #197
(context badge #189 / e2e in CI #187 / inline MCP test #170):

- server: extract the duplicated chatContextWindow ::text->positive-int
  coercion (resolve() + getMasked()) into an exported parsePositiveInt
  helper and unit-test its branches (200000/1.9/0/-5/""/abc/undefined),
  closing the untested read-path gap.
- client: merge the two backward scans over messageRows into one pure,
  exported selectContextBadge helper (numerator and denominator still
  taken from the most recent row carrying EACH value) and unit-test the
  different-rows and fresh-zero-doesn't-shadow cases.
- client: extract the MCP "Test" button tristate presentation into a pure
  mcpTestButtonView helper (collapses the two parallel if/else chains) and
  unit-test idle/ok-with-tools/ok-no-tools/failed label+tooltip branches.
- ci: redirect the backgrounded prod server's stdout/stderr to a log file
  in e2e-mcp and cat it on failure, so a start-up crash is diagnosable
  instead of surfacing only as the generic health timeout.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Ghost added 1 commit 2026-06-26 17:37:21 +03:00
The per-row MCP Test button derived its presentation solely from the test
mutation's data ({ ok, tools } | { ok, error }). When the request itself
rejected (401/403/500/network) there is no payload, so the row silently spun
back to the idle "Test" instead of reporting the failure.

Feed the mutation error into mcpTestButtonView so a reject also renders a red
"Failed", with the tooltip taken from the server message
(error.response.data.message) or a generic i18n fallback. Enable the tooltip
for any non-idle state. Cover the reject branch (with and without a server
message) in the helper unit test.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Owner

Code review (re-review) — PR #197: батч context badge (#189) + e2e в CI (#187) + inline MCP test (#170)

Вердикт: Approve. Дельта — это чистый test-coverage + поведенчески-нейтральный рефакторинг: две вынесенные pure-функции (selectContextBadge, mcpTestButtonView) и серверный хелпер parsePositiveInt, каждый теперь покрыт юнит-тестами, плюс захват лога сервера в CI. Новых блокеров нет, прошлые замечания закрыты.

Ре-ревью дельты 9b61024b..ba5cd024 (9 файлов, +357/−85). Аспекты: security, stability, conventions, documentation, regressions, test-coverage (параллельные ревьюеры + judge).

Статус прошлых блокеров

  • Прошлый ревью был Approve with comments без блокеров; основное замечание — нехватка тестов на новую логику бейджа/MCP-теста/числовой коэрции. Закрыто по существу: добавлены context-badge.test.ts (8 кейсов), ai-mcp-server-test-view.test.ts (4 состояния), ai-settings.service.spec.ts (8 кейсов для parsePositiveInt).
  • Риск numeric typing chatContextWindow (::text → число): семантика Number.isFinite && > 0 + Math.floor сохранена дословно, продублированный inline-код заменён единым parsePositiveInt, дрейф > 0>= 0 теперь ловится тестом. Закрыто.
  • Риск MCP external-call safety: дельта не трогает сетевой путь — выносится только презентация кнопки; тип IAiMcpServerTestResult (error уже санитизирован сервером) не изменён. Не затронуто.
  • i18n: новых строк нет — "OK · {{n}}", "No tools available", "Failed", "Test" проходят через тот же t() с уже существующими ключами. Закрыто.

Must fix before merge

Нет.

Non-blocking

Нет. (Проверено против исходников: selectContextBadge точно воспроизводит прежний двойной backward-scan с независимым подбором числителя/знаменателя и legacy-usage-fallback; metadata: {...} | null делает row(null) в тесте типобезопасным; серверный .spec.ts соответствует jest-конвенции каталога.)

Test coverage

Покрыто. Вся новая/изменённая логика дельты сопровождается тестами: бейдж (пустой ввод, оба значения из свежей строки, legacy-fallback, числитель/знаменатель с разных строк, не-затенение свежим нулём, пропуск null-metadata, без клампа); MCP-кнопка (idle/ok-с-tools/ok-без-tools/failed + ветки тултипа); parsePositiveInt (целое/дробь/0/отрицательное/пустое/нечисловое/undefined/null/number). Изменения ai-chat-window.tsx, ai-mcp-servers.tsx, ai-settings.service.ts — делегация в покрытые хелперы; CI-правка develop.yml (> /tmp/server.log 2>&1 + dump on failure) логики не несёт.

## Code review (re-review) — PR #197: батч context badge (#189) + e2e в CI (#187) + inline MCP test (#170) **Вердикт: Approve.** Дельта — это чистый test-coverage + поведенчески-нейтральный рефакторинг: две вынесенные pure-функции (`selectContextBadge`, `mcpTestButtonView`) и серверный хелпер `parsePositiveInt`, каждый теперь покрыт юнит-тестами, плюс захват лога сервера в CI. Новых блокеров нет, прошлые замечания закрыты. _Ре-ревью дельты `9b61024b..ba5cd024` (9 файлов, +357/−85). Аспекты: security, stability, conventions, documentation, regressions, test-coverage (параллельные ревьюеры + judge)._ ### Статус прошлых блокеров - Прошлый ревью был Approve with comments без блокеров; основное замечание — нехватка тестов на новую логику бейджа/MCP-теста/числовой коэрции. Закрыто по существу: добавлены `context-badge.test.ts` (8 кейсов), `ai-mcp-server-test-view.test.ts` (4 состояния), `ai-settings.service.spec.ts` (8 кейсов для `parsePositiveInt`). - Риск numeric typing `chatContextWindow` (`::text` → число): семантика `Number.isFinite && > 0` + `Math.floor` сохранена дословно, продублированный inline-код заменён единым `parsePositiveInt`, дрейф `> 0`→`>= 0` теперь ловится тестом. Закрыто. - Риск MCP external-call safety: дельта не трогает сетевой путь — выносится только презентация кнопки; тип `IAiMcpServerTestResult` (`error` уже санитизирован сервером) не изменён. Не затронуто. - i18n: новых строк нет — `"OK · {{n}}"`, `"No tools available"`, `"Failed"`, `"Test"` проходят через тот же `t()` с уже существующими ключами. Закрыто. ### Must fix before merge Нет. ### Non-blocking Нет. (Проверено против исходников: `selectContextBadge` точно воспроизводит прежний двойной backward-scan с независимым подбором числителя/знаменателя и legacy-`usage`-fallback; `metadata: {...} | null` делает `row(null)` в тесте типобезопасным; серверный `.spec.ts` соответствует jest-конвенции каталога.) ### Test coverage Покрыто. Вся новая/изменённая логика дельты сопровождается тестами: бейдж (пустой ввод, оба значения из свежей строки, legacy-fallback, числитель/знаменатель с разных строк, не-затенение свежим нулём, пропуск null-metadata, без клампа); MCP-кнопка (idle/ok-с-tools/ok-без-tools/failed + ветки тултипа); `parsePositiveInt` (целое/дробь/0/отрицательное/пустое/нечисловое/undefined/null/number). Изменения `ai-chat-window.tsx`, `ai-mcp-servers.tsx`, `ai-settings.service.ts` — делегация в покрытые хелперы; CI-правка `develop.yml` (`> /tmp/server.log 2>&1` + dump on failure) логики не несёт.
vvzvlad merged commit 580f7bd5bb into develop 2026-06-26 18:09:48 +03:00
Sign in to join this conversation.
No Reviewers
2 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: vvzvlad/gitmost#197