feat(ai,mcp): идентичность прогона реиндекса + брендированные PageId/SlugId (#495 item 14)
Аспект A — идентичность прогона реиндекса эмбеддингов. У статуса/поллинга реиндекса не было идентичности КОНКРЕТНОГО прогона, из-за чего класс багов «это тот же прогон или новый?» чинили дважды. Теперь каждый прогон получает свой runId (crypto.randomUUID в start()), он хранится в Redis- хэше рядом с total/done/startedAt и возвращается в ReindexProgress. Эндпоинт статуса (getMasked -> MaskedAiSettings) отдаёт runId и reindexStartedAt. Клиент кеит поллинг на (runId, startedAt): смена runId = НОВЫЙ прогон (сбрасываем залатанное per-run состояние поллинга), тот же runId = тот же прогон. Всё best-effort/косметика как и остальной код прогресса: пустой/отсутствующий runId деградирует мягко и никогда не ломает реиндекс. Аспект B — брендированные PageId/SlugId в MCP-клиенте + валидация формата в серверных DTO (семейство инцидентов #435: двойная идентичность страницы — внутренний id и slugId — гонялась как голая строка и молча путалась). - packages/mcp/src/lib/page-id.ts: номинальные типы PageId/SlugId + валидирующие конструкторы asPageId/asSlugId и гварды isPageId/isSlugId (формат UUID и 10-символьного slugId). PageId протянут через единственный узел канонизации и записи: resolvePageId() теперь возвращает PageId, а ключ per-page лока (withPageLock) и точки записи в collab (mutatePageContent/replacePageContent/ updatePageContentRealtime) требуют бренд — сырой slugId/непроверенный id больше не проходит проверку типов (инвариант #260 «resolve-then-lock» теперь на уровне компилятора). Публичный вход методов остаётся строкой (это legitimно UUID ИЛИ slugId), брендируется каноническое значение. - apps/server core/page/dto: PageIdDto.pageId получает валидацию формата (@Matches, UUID или 10-символьный slugId), так что кривая/подменённая идентичность отклоняется на границе, а не падает в repo голой строкой. Тесты (все зелёные, с mutation-verify каждого): - server: getMasked отдаёт runId/reindexStartedAt; стор пишет/читает runId и мягко деградирует его до ''; DTO отклоняет кривой pageId/slugId и принимает валидный. - client: чистый хелпер reindexRunKey/isNewReindexRun (кеинг поллинга на runId). - mcp: конструкторы/гварды PageId/SlugId (валидация формата). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -51,6 +51,14 @@ export interface IAiSettings {
|
||||
// True while a full workspace reindex is actively running; the counts above
|
||||
// then reflect the live run progress (done climbs 0 -> total).
|
||||
reindexing?: boolean;
|
||||
// Identity of the ACTIVE reindex run (present only while `reindexing`). The
|
||||
// poll keys on `runId`: a changed value means a NEW run (reset the per-run
|
||||
// poll state the UI latched), the same value is the run already being watched.
|
||||
// Absent/empty ('') => no identity available; the client keeps prior behaviour.
|
||||
runId?: string;
|
||||
// Epoch-ms the active run started; paired with `runId` so a restart with a
|
||||
// recycled id is still detected as a new run.
|
||||
reindexStartedAt?: number;
|
||||
}
|
||||
|
||||
// Update payload. Key semantics (same for `apiKey` and `embeddingApiKey`):
|
||||
|
||||
Reference in New Issue
Block a user