Reference in New Issue
Block a user
Delete Branch "feat/443-get-page-context"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Summary
Финальная, третья часть #443. Новый тул
getPageContext— поpageId(из поиска, из ссылки) вернуть «где я / что вокруг»: сама страница + breadcrumbs + прямые дети, только метаданные, одним вызовом. Раньше этоget_page+ полное дерево. Только чтение. closes #443 (все три части: search #468, getTree #469, этот).Стоит стопкой на части 2 (
feat/443-get-tree, PR #469) — база указывает на getTree-ветку, в диффе только getPageContext. Порядок: … #468 search → #469 getTree → #470 getPageContext.Реализация (бэкенд готов — серверных изменений нет)
client.tsgetPageContext(pageId):resolvePageId(slugId→uuid; для uuid без round-trip) →POST /pages/breadcrumbs+listSidebarPages(spaceId, uuid). Два запроса для uuid-входа (spaceId из ответа breadcrumbs), +1 для slugId.{page:{pageId,title,spaceId}, breadcrumbs:[{pageId,title}] (root→parent; [] для корня), children:[{pageId,title,hasChildren}]}.getPageBreadCrumbs: CTE сидится на childPageId → вверх →.reverse()→ root→page; страница — ПОСЛЕДНИЙ элемент):page = chain[last],breadcrumbs = chain.slice(0,-1), корень →[]. Проекция явная — наружу толькоpageId(UUID),slugId/icon/positionне утекают.children— прямые дети черезlistSidebarPages(cursor-пагинация #442/#451): страница с >20 детьми отдаёт всех без дублей, порядок поposition.getPageContextна ОБОИХ хостах;mcpName===inAppKey==="getPageContext"(camelCase);DocmostClientLike/DocmostClientMethod+= it; compile-time contract;TOOL_FAMILYREAD + routing-проза.How verified
mcptscчисто;node --test— 725/725 (6 новых get-page-context: 3-й уровень split, no-slugId-leak + 2 запроса, корень→[], slugId-resolve, >20 детей без cap/дублей, плохой id→ошибка, пустой chain→throw; +1 tool-specs);prosemirror-markdowntsc чисто; server jest (shared-tool-specs.contract+ai-chat-tools.service) 265/265.getTreeструктурная (общий sidebar/position-источник детей, тот же UUIDpageId).Checklist
Часть 3 из 3 по #443 (getPageContext) — финальная, closes #443. Стоит стопкой на части 2 (PR #469, feat/443-get-tree) — база указывает на getTree-ветку, в диффе только getPageContext. Мержить после #469.
Что сделал: getPageContext(pageId) — page + breadcrumbs + прямые дети одним вызовом, два запроса (breadcrumbs + sidebar). Порядок breadcrumbs выверил по серверному источнику (страница — последний элемент, root→page), не по памяти. Наружу только pageId. Плохой/недоступный id → явная ошибка, не пустой объект.
Проверка: mcp node --test 725/725 (6 новых), tsc чисто, server jest (contract + ai-chat) 265/265.
Внутренний цикл: 1 проход (адверсарно, 7 рисков — главные: split breadcrumbs+envelope и error-not-hollow — оба проверены против фактического page.service.ts::getPageBreadCrumbs, не по резюме). Блокеров нет; единственная находка — косметический юникод-апостроф, правки не требует.
Итого #443: три read-тула (search подстрочный, getTree, getPageContext), собраны стопкой search→getTree→getPageContext. Мержить в этом порядке после breaking-окна (#463→#466→#467).
Ревью — #470
feat(mcp): getPageContext — «где я / что вокруг» по pageId (#443, часть 3/3, closes #443)Вердикт: PASS ✅
Полный веер из 9 аспектов по изолированной дельте (база — ветка #443-get-tree/#469; ревьюил
bfb4c8d8..5dc7a270= 7 файлов, +502/−1, mcp-only). Финал триады #443. Гейт зелёный.Проверено — breadcrumbs-порядок/no-leak/edge-cases (клик)
getPageContext(pageId)→{page, breadcrumbs, children}одним вызовом.resolvePageId(UUID short-circuit, slugId +1 кэш) →POST /pages/breadcrumbs(цепочка root→page, page ПОСЛЕДНИЙ) +listSidebarPages(прямые дети)..reverse()→ root→page; клиентself = chain[last],breadcrumbs = chain.slice(0,-1)(root→parent),[]для корня. Адверсариально: сломалslice(0,-1)→slice(0)→ 3 теста КРАСНЕЮТ (deep-page/root-empty/slugId), «empty chain throws» корректно остался зелёным. Порядок load-bearing.pageId/title(/hasChildren), БЕЗ slugId/icon/position. Тест ассертит отсутствие «Slug» во всём дампе.{}); корень →breadcrumbs:[]; без детей →children:[]; дети деду́плены + порядок сохранён через курсор-пагинацию (>20 детей, регресс против #442). Request-count: 2 (UUID) / 3 (slugId) — заассерчено моком.🔎 Вне scope — отдельная задача (НЕ часть вердикта #470) → issue #471
[security, server, ПРЕД-СУЩЕСТВУЮЩАЯ]
getPageBreadCrumbs(page.service.ts:1072) — рекурсивный CTE поднимается по предкам, фильтруя ТОЛЬКОdeletedAt, БЕЗ page-permission-фильтра; контроллер валидирует только целевую страницу. В гранулярной модели → утечка title ограниченного предка./pages/breadcrumbsУЖЕ зовёт веб-UI (space-tree.tsx:128) с той же авторизацией — breadcrumb-бар уже показывает эти title юзеру. getPageContext зовёт тот же эндпоинт в той же CASL-области — не вводит и не усугубляет (агент видит только title, открыть предка не может — permission на getPage/getNode сохранён).filterAccessiblePageIds*, как sidebar/tree). Понизил security-агентский «CRITICAL/DO NOT MERGE» → out-of-scope: пред-существующее, web-UI-эквивалент, не блокирует #470.⛔ DROP — кодеру НЕ делать
[below-threshold]low[documentation] нет CHANGELOG-энтри (паттерн общий с #468/#469; аддитивная фича, задокументирована в tool-desc + ROUTING_PROSE + doc-комменте). Рекомендую скоординированную CHANGELOG-запись на всю триаду #443.DO/ESCALATE нет. breadcrumbs-порядок сверен с сервером + адверсариально пришпилен, pageId-only-инвариант enforced+tested, edge/error/dedup/request-count покрыты, аддитивно. Единственная security-находка — ПРЕД-СУЩЕСТВУЮЩАЯ серверная (issue #471), не #470. Гейт зелёный. Approve. Триада #443 (search/getTree/getPageContext) завершена.
Часть 3/3 #443, на #469. 9 аспектов. breadcrumbs-slice адверсариально краснит 3 теста; no-slugId/edge/dedup/request-count пришпилены. Ancestor-permission-leak — пред-существующая серверная (не трогает server), web-UI-эквивалент → out-of-scope, issue #471. Merge-order: после #469. Escalate нет.