You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
anchor-integrity/fix.mjs объявляет «ambiguous» и отказывается чинить целый класс поломок, который на самом деле чинится детерминированно, без LLM. Класс системный: он порождён собственной конвенцией спек-генератора ({#id} в заголовках), поэтому воспроизводится по всему корпусу, а не в одном файле.
Причина A — литеральный {#id} уезжает в slug. GLFM явные id не поддерживает: текст {#phase-0-refactoring} считается частью заголовка, поэтому slug получается phase-0-refactoring-phase-0-refactoring, а ссылка ведёт на #phase-0-refactoring. Достаточно снять суффикс {#...} — и slug становится ровно тем, что уже написано в ссылке.
Что {#id} инертны, подтверждается самим spec-сервером: read_spec_doc {section: "phase-05-implement-red-phase-stubs"} → SECTION_NOT_FOUND, а {section: "Phase 0: Refactoring {#phase-0-refactoring}"} (полный текст вместе с литералом) → находит. То есть графом эти id не используются, удаление ничего не отвязывает.
Причина B — декоративное длинное тире даёт двойной дефис. Для Phase 1–4 снятия {#id} мало: Phase 1: P0 — Billing Fixes → пунктуация удаляется, два пробела вокруг тире превращаются в два дефиса → phase-1-p0--billing-fixes, а ссылка ведёт на phase-1-p0-billing-fixes. Убрать тире (Phase 1: P0 Billing Fixes) — slug сходится.
Именно на этой развилке (править заголовок или править ссылку) фиксер, судя по поведению, и объявляет ambiguous. Но развилка разрешается правилом, а не рассуждением.
Как это чинится детерминированно
Для каждой битой ссылки #target в том же документе:
Собрать заголовки-кандидаты, чей slug совпал бы с #target после применения нормализаций-удалений, каждая из которых убирает только несемантический мусор:
удалить хвостовой {#...};
удалить декоративные разделители (—, –, ·, |) вместе с окружающими пробелами;
схлопнуть повторные пробелы.
Если ровно один кандидат — применить, это не ambiguous.
Если ноль или больше одного — только тогда ambiguous / LLM.
Ключевое: правки такого рода — чистые удаления оформительского мусора, они не меняют смысла заголовка и не трогают ссылки. Риск нулевой, а покрывают они, судя по списку гейта (24 спеки в этом репозитории), почти весь класс.
Я применил это правило руками — шесть заголовков, ничего кроме них:
Реализовать нормализации выше в fix.mjs как детерминированный слой (до LLM-фоллбэка). Тогда fixable станет 6 вместо 0 и гейт перестанет блокировать сессии из-за оформительского мусора.
Определиться с конвенцией {#id} один раз. Сейчас генератор спек их эмитит (весь корпус ими усыпан: {#task-0-1}, {#phase-*}), а anchor-чекер и read_spec_doc их не признают. Либо чекер честно поддерживает {#id} (как pandoc/kramdown) — тогда эти ссылки не битые вовсе; либо генератор перестаёт их писать и существующие снимаются миграцией. Пока обе стороны не согласованы, гейт будет ловить собственный хвост.
fix.mjs должен объяснять «ambiguous», а не просто считать. Сейчас вывод fixable=0 ambiguous=6 не говорит ни какие кандидаты рассматривались, ни почему выбор не сделан. Агенту приходится реверсить правила слагификации, чтобы понять, что вообще не так.
Суть
anchor-integrity/fix.mjsобъявляет «ambiguous» и отказывается чинить целый класс поломок, который на самом деле чинится детерминированно, без LLM. Класс системный: он порождён собственной конвенцией спек-генератора ({#id}в заголовках), поэтому воспроизводится по всему корпусу, а не в одном файле.Что произошло
Гейт FR-34 на Stop заблокировал сессию:
Штатный фиксер сдался:
Между тем все шесть ссылок в оглавлении были написаны правильно — сломаны были заголовки. Вот они дословно:
56: ## Phase 0: Refactoring {#phase-0-refactoring} 112: ## Phase 0.5: Implement Red Phase Stubs {#phase-05-implement-red-phase-stubs} 156: ## Phase 1: P0 — Billing Fixes {#phase-1-p0-billing-fixes} 220: ## Phase 2: P1 — New Endpoints {#phase-2-p1-new-endpoints} 282: ## Phase 3: P2 — Message Features {#phase-3-p2-message-features} 356: ## Phase 4: P3 — Advanced {#phase-4-p3-advanced}Две независимые причины, обе механические
Причина A — литеральный
{#id}уезжает в slug. GLFM явные id не поддерживает: текст{#phase-0-refactoring}считается частью заголовка, поэтому slug получаетсяphase-0-refactoring-phase-0-refactoring, а ссылка ведёт на#phase-0-refactoring. Достаточно снять суффикс{#...}— и slug становится ровно тем, что уже написано в ссылке.Что
{#id}инертны, подтверждается самим spec-сервером:read_spec_doc {section: "phase-05-implement-red-phase-stubs"}→SECTION_NOT_FOUND, а{section: "Phase 0: Refactoring {#phase-0-refactoring}"}(полный текст вместе с литералом) → находит. То есть графом эти id не используются, удаление ничего не отвязывает.Причина B — декоративное длинное тире даёт двойной дефис. Для Phase 1–4 снятия
{#id}мало:Phase 1: P0 — Billing Fixes→ пунктуация удаляется, два пробела вокруг тире превращаются в два дефиса →phase-1-p0--billing-fixes, а ссылка ведёт наphase-1-p0-billing-fixes. Убрать тире (Phase 1: P0 Billing Fixes) — slug сходится.Именно на этой развилке (править заголовок или править ссылку) фиксер, судя по поведению, и объявляет ambiguous. Но развилка разрешается правилом, а не рассуждением.
Как это чинится детерминированно
Для каждой битой ссылки
#targetв том же документе:#targetпосле применения нормализаций-удалений, каждая из которых убирает только несемантический мусор:{#...};—,–,·,|) вместе с окружающими пробелами;Ключевое: правки такого рода — чистые удаления оформительского мусора, они не меняют смысла заголовка и не трогают ссылки. Риск нулевой, а покрывают они, судя по списку гейта (24 спеки в этом репозитории), почти весь класс.
Я применил это правило руками — шесть заголовков, ничего кроме них:
Результат вашей же проверялкой:
Что просится в фикс
Реализовать нормализации выше в
fix.mjsкак детерминированный слой (до LLM-фоллбэка). Тогдаfixableстанет 6 вместо 0 и гейт перестанет блокировать сессии из-за оформительского мусора.Определиться с конвенцией
{#id}один раз. Сейчас генератор спек их эмитит (весь корпус ими усыпан:{#task-0-1},{#phase-*}), а anchor-чекер иread_spec_docих не признают. Либо чекер честно поддерживает{#id}(как pandoc/kramdown) — тогда эти ссылки не битые вовсе; либо генератор перестаёт их писать и существующие снимаются миграцией. Пока обе стороны не согласованы, гейт будет ловить собственный хвост.fix.mjsдолжен объяснять «ambiguous», а не просто считать. Сейчас выводfixable=0 ambiguous=6не говорит ни какие кандидаты рассматривались, ни почему выбор не сделан. Агенту приходится реверсить правила слагификации, чтобы понять, что вообще не так.Смежное, уже заведено отдельно — Дедлок: anchor-гейт требует починки, а MCP-дверь блокирует запись из-за 135 несвязанных pre-existing находок #207: даже когда фикс известен, записать его через MCP-дверь нельзя, если в документе есть несвязанные pre-existing нарушения формы (у меня — 135 находок
task: missing Done When block). Мне пришлось воспользоваться логируемым escape[skip-spec-access: ...]. Пока Дедлок: anchor-гейт требует починки, а MCP-дверь блокирует запись из-за 135 несвязанных pre-existing находок #207 не починен, детерминированный фиксер из пункта 1 всё равно будет упираться вwritten=0на таких файлах — эти два бага надо чинить парой, иначе фикс не долетит до диска.Окружение
stgmt/dev-pomogator@2.0.4, Node v24.17.0, Windows 11,SPEC_ACCESS_ENFORCE=true, репозиторийlm-saas.