fix(ci,mcp): REGISTRY_STAMP в билде + кросс-пакетный CI — закрыть skew build/vs/src (#447) #457
Open
agent_coder
wants to merge 1 commits from
fix/447-registry-stamp-ci into develop
pull from: fix/447-registry-stamp-ci
merge into: vvzvlad:develop
vvzvlad:main
vvzvlad:feat/424-drawio-rules
vvzvlad:fix/409-invalid-node-validation
vvzvlad:refactor/448-generate-inventory
vvzvlad:refactor/445-execute-mapping
vvzvlad:refactor/446-derive-client-types
vvzvlad:fix/444-agent-loop-guards
vvzvlad:fix/396-sendnow-detached-run
vvzvlad:fix/452-immutable-cache-control
vvzvlad:develop
vvzvlad:docs/footnote-authoring-comment-cleanup
vvzvlad:feat/437-error-diagnostics
vvzvlad:fix/442-cursor-pagination
vvzvlad:feat/419-footnote-normalize
vvzvlad:perf/399-comment-resolve-async
vvzvlad:perf/435-collab-token-cache
vvzvlad:perf/344-background-rerenders
vvzvlad:perf/348-backend-lowhanging
vvzvlad:feat/423-drawio-crud
vvzvlad:perf/401-collab-hotpath
vvzvlad:feat/430-live-reconnect
vvzvlad:perf/400-collab-session
vvzvlad:refactor/414-dedup-node-ops
vvzvlad:feat/417-new-comments-signal
vvzvlad:feat/408-createcomment-hints
vvzvlad:feat/407-persist-tool-errors
vvzvlad:feat/420-footnote-render
vvzvlad:dummy-review/mcp-hang-fix
vvzvlad:dummy-review/mcp-hang-base
vvzvlad:feat/381-resumable-sse-pr1
vvzvlad:feat/git-sync-2
vvzvlad:docs/agents-workspace-build-order
vvzvlad:feature/offline-sync
vvzvlad:perf/342-code-splitting
vvzvlad:perf/346-compression-cache
vvzvlad:feat/196-multi-cursor
vvzvlad:feat/370-page-versioning
vvzvlad:perf/343-typing-latency
vvzvlad:docs/how-to-test
vvzvlad:fix/ai-sdk-partial-output-oom
vvzvlad:test/351-generative-converter
vvzvlad:feat/371-roles-catalog
vvzvlad:refactor/345-server-converter
vvzvlad:refactor/294-spec-registry-cont
vvzvlad:fix/363-migration-order
vvzvlad:fix/e2e-callout-and-gate-build
vvzvlad:fix/docker-re2-toolchain
vvzvlad:feat/git-sync
vvzvlad:fix/media-roundtrip-stability
vvzvlad:fix/340-comment-panel-perf
vvzvlad:fix/332-deferred-tools
vvzvlad:fix/329-ephemeral-suggestions
vvzvlad:fix/330-search-in-page
vvzvlad:fix/328-resolved-anchor-spam
vvzvlad:fix/331-intraline-diff
vvzvlad:fix/324-coverage-gate
vvzvlad:fix/325-mobile-390
vvzvlad:feat/293-A-git-sync-package
vvzvlad:feat/300-avatar-oklch
vvzvlad:fix/321-banner-mobile
vvzvlad:feat/300-avatar-colors
vvzvlad:feat/315-comment-suggestions
vvzvlad:feat/scroll-restore-stable-wait
vvzvlad:feat/300-agent-avatar-stack
vvzvlad:feat/300-avatar-polish
vvzvlad:refactor/294-tool-spec-registry
vvzvlad:feat/scroll-restore-ux
vvzvlad:fix/responsive-tablet-sidebar
vvzvlad:feature/ai-chat-page-change-observability
vvzvlad:image-inline-center
vvzvlad:fix/283-short-remap-title
vvzvlad:fix/283-slash-layout
vvzvlad:image-inline-row
vvzvlad:feat/276-ai-chat-dock
vvzvlad:fix/269-table-menu-refocus
vvzvlad:docs/dev-stand-guide
vvzvlad:feat/266-scroll-position
vvzvlad:fix/260-collab-docname-slugid
vvzvlad:test/244-phase2-tail
vvzvlad:fix/262-reindex-progress-realtime
vvzvlad:fix/258-changelog-compare-links
vvzvlad:fix/244-dataloss-bugs
vvzvlad:feat/246-spoiler
vvzvlad:feat/221-image-captions
vvzvlad:test/244-part-b
vvzvlad:feat/251-intentional-clear
vvzvlad:fix/embeddings-reindex-progress
vvzvlad:refactor/193-tool-spec-registry
vvzvlad:fix/255-ws-redis-adapter-leak
vvzvlad:fix/252-e2e-open-handles
vvzvlad:feat/229-catalog-yaml
vvzvlad:feat/243-blob-sandbox
vvzvlad:feat/228-inline-footnotes
vvzvlad:fix/qa-ui-bugs-216-218
vvzvlad:feature/agent-roles-catalog
vvzvlad:fix/share-alias-rename
vvzvlad:fix/ai-chat-empty-render
vvzvlad:feat/191-chat-doc-binding
vvzvlad:feat/201-temporary-notes
vvzvlad:feat/198-interrupt-agent
vvzvlad:feat/ai-chat-full-history
vvzvlad:feat/199-ai-generate-title
vvzvlad:feat/205-share-aliases
vvzvlad:batch/issues-189-187-170
vvzvlad:feat/170-mcp-test-button
vvzvlad:feat/189-context-badge
vvzvlad:feat/198-interrupt-agent-send-now
vvzvlad:fix/issues-190-159
vvzvlad:fix/ai-chat-new-chat-during-stream
vvzvlad:fix/ai-chat-stream-perf
vvzvlad:batch/issues-2026-06-25
vvzvlad:feat/ai-chat-persistent-history
vvzvlad:fix/ai-chat-copy-chat-wysiwyg
vvzvlad:fix/ai-stream-reset-resilience
vvzvlad:fix/ai-stream-undici-timeout
vvzvlad:fix/footnote-review-1227-followup
vvzvlad:fix/ai-chat-token-counter-realtime
vvzvlad:docs/manual-qa-test-plan
No Reviewers
Labels
Clear labels
epic
needs-human
review/approved
review/changes-requested
review/needs
возможно, баг
Large multi-phase effort spanning many changes
эскалация: нужно решение человека
в последнем ревью нет открытых blocking-находок
последнее ревью оставило открытые blocking-находки
head не ревьюился (head != reviewed_head)
Похоже на баг, но требует подтверждения/воспроизведения; чиним, если повторится
No Label
review/approved
Milestone
No items
No Milestone
Projects
Clear projects
No project
No Assignees
Notifications
Due Date
No due date set.
Dependencies
No dependencies set.
Reference: vvzvlad/gitmost#457
Reference in New Issue
Block a user
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Delete Branch "fix/447-registry-stamp-ci"
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
Закрывает два структурных слепых пятна tool-инфраструктуры (первый линк Фазы 1б роадмапа #416). closes #447
1. Skew
build/vssrc/. Рантайм сервера грузит СКОМПИЛИРОВАННЫЙpackages/mcp/build/index.js, а серверные guard-тесты (shared-tool-specs.contract,tool-tiers) читаютpackages/mcp/src/tool-specs.ts. Правкаsrcбез пересборки → тесты зелёные, работающий сервер расходится со спеками.2. Guard в чужом пакете. Спеки добавляют в
packages/mcp, а parity-тесты живут в jest-сьютеapps/server— PR, трогающий только пакет, проходит зелёным, сломанная in-app-проводка всплывает уже на develop (кейс f46d89ea/PR #434).Механика:
scripts/gen-registry-stamp.mjsсчитает sha256 от нормализованного (CRLF→LF, один хвостовой\nснят) сырого текстаsrc/tool-specs.ts→ пишетsrc/registry-stamp.generated.ts(gitignored),index.tsреэкспортит → стамп вbuild/. Вшит вbuild/pretest/watchДОtsc. Выбор текста (а не semantic hash) —buildShapeнесериализуем, а схема ровно то, что должно синкаться; over-strict безопасен.computeSrcRegistryStampпересчитывает изsrcрядом сbuild/index.js(dev-vs-prod поexistsSync, любая ошибка →null), при рассинхроне бросает@docmost/mcp build is stale — run: pnpm --filter @docmost/mcp build. В prod (нетsrc) и на pre-#447 билдах (нетREGISTRY_STAMP) — чистый no-op (крах старта исключён).mcp-server-parityсобирает shared-deps+mcp (регенерит стамп), гоняет ОБА сьюта вместе (mcpnode --test+ server guard-спеки) — именованный гейт, нельзя случайно расщепить.@docmost/mcp.Примечание: после #445 (автопроводка) класс «забыли провести in-app» умрёт по построению, но stamp остаётся полезен — ловит любой дрейф стейл-артефакта (описания/схемы/tier), а кросс-пакетный CI — любые будущие межпакетные контракты.
How verified
Демонстрация (честный путь через реальный
loadDocmostMcp): свежий билд → LOADED OK; правка описания вsrc/tool-specs.tsБЕЗ ребилда → THREW «build is stale»; ребилд → LOADED OK; prod-симуляция (src убран) → LOADED OK (no-op).pnpm --filter @docmost/mcp build→ чисто;node --test test/unit/registry-stamp.test.mjs→ 10/10;jest docmost-client.loader shared-tool-specs.contract tool-tiers→ 3 suites, 228 passed.Порядок мержа (Фаза 1б, #416)
Это первый линк. Дальнейшие линки (#446 типы, #445 execute-маппинг, #448 генерация инвентаря) пойдут стопкой; сводный порядок мержа опишу комментарием в #416, когда стопка будет собрана. #447 независим — мержится первым или в любом месте до #445.
Checklist
Ревью — #457
fix(ci,mcp): REGISTRY_STAMP в билд-артефакте + кросс-пакетный CI — закрыть skew build/ vs src/ (#447)Вердикт: PASS ✅
Полный веер из 9 аспектов (merge-base
f8d37d89; 9 файлов, +496). Гейт зелёный в чистой комнате (Node 22 = CI).Механизм skew-гарда — проверено end-to-end (клик)
Проблема (#447): сервер грузит скомпилированный
packages/mcp/build/, а parity/tier-тесты читаютsrc/tool-specs.ts. Правка спека без ребилда → build/ и src/ молча расходятся, тесты зелёные, сервер отдаёт СТАРЫЕ тулы. PR закрывает это:gen-registry-stamp.mjs(передtscвbuild/watch/pretest) хеширует ТЕКСТtool-specs.ts→ gitignoredregistry-stamp.generated.ts, ре-экспортREGISTRY_STAMPпопадает в build/. Хеш по сырому тексту (не структуре) намеренно —buildShape(zod-схемы) несериализуемы, а схема — ровно то, что должно оставаться в синхроне; текст ловит любую правку (имя/описание/tier/catalog/схема).docmost-client.loader.ts) пересчитывает хеш из src и бросает@docmost/mcp build is stale …при расхождении. В prod src отсутствует →computeSrcRegistryStamp→ null → чистый no-op (детект по НАЛИЧИЮ файла, не NODE_ENV).tool-specs.tsпосле билда → src≠build (гард бы сработал); восстановил → снова совпал. Предикат из 3 условий (src не null ИREGISTRY_STAMPстрока И расхождение) корректен во всех 4 ветках: prod-no-src / pre-#447-без-стампа / свежий билд / stale — покрыты тестами.mcp-server-parity(БЕЗpaths:-фильтра — намеренно): гоняет mcpnode --testИ серверные tool-guard jest-спеки вместе, ребилдя mcp первым. Coupling явный, path-фильтром его не расщепить.Объективный гейт (клин-рум /home/claude/gm-ci)
registry-stamp.test.mjs10/10: cross-impl-хеш683376e2…+ desync-guard «built REGISTRY_STAMP == пересчёт из src»).docmost-client.loader.spec+shared-tool-specs.contract+tool-tiers+ai-chat-tools.service,--runInBand).\nидентична в кодгене и загрузчике; расхождение любой стороны краснит один из двух тестов).⛔ DROP — кодеру НЕ делать (лог калибровки)
[by-design]low[coherence] правки tool-specs.ts только по форматированию (пробелы/комменты) тоже меняют хеш → форсят ребилд. Консервативно и безопасно: нет ложных негативов (реальные правки всегда ловятся), ложный позитив = дешёвый ребилд. Приемлемо.[out-of-scope]low[regressions] на свежем клоне server tsc падает, пока mcp не собран (build/ gitignored) — пред-существующее и задокументировано в AGENTS.md (build-order), PR не вводит.[by-design]low[architecture] дублирование normalize+sha256 (ESM-кодген vs CJS-загрузчик) — обоснованно (CJS-сервер не импортит ESM-скрипт), дрейф стережёт load-bearing cross-impl тест на обеих сторонах.DO/ESCALATE нет. Механизм закрывает skew полностью (адверсариально подтверждено), prod-no-op и pre-#447-совместимость держатся, все пути ребилдят стамп до tsc, coupling закреплён CI-джобой. Гейт зелёный. Approve.
9 аспектов (security+stability, regressions+coherence, testcov+conv+docs+simp+arch — все LGTM). Skew-гард проверен end-to-end мутацией src после билда; 4 ветки предиката, cross-impl хеш и desync-guard — объективно зелёные. Escalate нет.
View command line instructions
Checkout
From your project repository, check out a new branch and test the changes.