Skip to content

fix(graph): resolve metadata paths across batch boundaries (BA-067) - #419

Merged
itrous merged 32 commits into
itrous:developfrom
defin85:fix-ba067-graph-source-root
Oct 15, 2026
Merged

itrous merged 32 commits into
itrous:developfrom
defin85:fix-ba067-graph-source-root

Conversation

@defin85

@defin85 defin85 commented Oct 14, 2026 •

Copy link
Copy Markdown
Contributor

Graph batches share the full BSL file set but load text and register file-to-root inputs only for the active batch. A borrowed extension form can inherit attributes from a base form in another batch; metadata lookup then required a missing batch-local mapping and panicked, aborting graph publication.

Path lookup now honors an explicit mapping first, then checks exact FileId membership in the published BSL root. This fallback does not load foreign text or revisions, search other roots, or weaken strict source/text getters. Tests cover inherited metadata, removal and restoration in FileSet, explicit-root priority, and graph equality across batch sizes.

Validation on current head 09fb4ef2: GitHub Linux Check (formatting, Clippy and full workspace tests), Corpus contract, and Windows MCP transports + secure broker all passed. The Windows job is its configured transport/cache/lease selection.

Before publication integration, the targeted borrowed-form tests (7), path-priority test (1), graph batch-size parity test, formatting and strict Clippy also passed. Full TN runtime graph publication and binary installation were not tested.

OniVe and others added 10 commits October 14, 2026 12:23
…ilt project (github#226, github#227)

A failed resident build was terminal for the life of the daemon: every later
call answered internal_error until the broker's idle TTL, and a build that
never finished held Loading forever. The resident now leaves Failed on its own
pacing (base 5 s doubling to a 60 s cap, env-tunable), the previous cause
stays visible while the retry runs, the build has a deadline that publishes a
Failed instead of waiting forever, and the outcome of an attempt the deadline
has retired can no longer overwrite whatever the live attempt published — the
retry also re-reads the token before it touches the hub cursor. The reading
tools answer a still-failed resident with one structured snapshot
(state/reason/generation/attempts/retry_after_ms/failed_age_ms) in both the
not-ready envelope and the RPC error's data, and the envelope's status
reflects the state again. The wire change moves the contract to 3.5.

A workspace built from source-set flags was covered only indirectly: the
nested-configuration fixture now pins that SharedState::workspace builds the
project through project::at, so reverting to Project::new fails the test.

Tests: the retry schedule and its clamps, the deadline (unit and sweeper), the
retired-attempt discards (success and failure, plus a retire-before-start),
the single-snapshot failure answer, the envelope's closed readiness
vocabulary, the cursor carried across a failed retry, and the boot
composition gate.
… snapshot failure reason

- run_load checks the attempt token and re-subscribes the hub cursor under one hold
  of `inner`, so a deadline retiring the attempt in between can no longer let the
  retired attempt replace the live attempt's subscription.
- retry_delay doubles step by step up to the configured cap instead of clamping the
  exponent at 20, which plateaued below a long BSL_MCP_DIAG_RETRY_CAP_SECS.
- failure_answer takes the reason from the same lifecycle snapshot as attempts and
  retry_after_ms; the error text of the five resident handlers uses it too.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The server numbers its own requests (window/workDoneProgress/create) from 1,
so a hover response awaited by bare id could match a server request with the
same id. The legacy-extension-scope test now waits through Lsp::request, which
requires a response shape, and a harness test pins that a same-id server
request is skipped.

wait_for_within dropped a message that arrived just past its deadline; it now
returns it to the pending queue for the next wait.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wait_for_within checked its deadline before reading the queue, so a wait
whose bound had already run out answered silence over a message that had
arrived, and one taken in just past the bound was set aside unjudged, which
wait_for_judging then reported as a silent server. The bound is now checked
after each message: unrelated traffic still cannot stretch a wait, and a
zero remainder polls once instead of seeing nothing.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
wait_for_judging checked its total ceiling before the predicate, so a
matching message delivered in time but taken in just past the ceiling, as a
late wake-up of the test thread leaves it, failed the wait instead of
answering it. The predicate now judges first; unrelated traffic still
cannot carry a wait past the ceiling.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
fix(mcp-server): leave a failed diagnostics build and pin the flag-built project (itrous#226, itrous#227)
@itrous

itrous commented Oct 14, 2026

Copy link
Copy Markdown
Owner

🤖 Начата автоматическая приёмка — этот pull request взят в работу ИИ-агентом:
изменения переносятся на актуальную целевую ветку, проходят независимое ревью
и соответствующие тесты, очевидные замечания правятся по ходу. Обновления появятся
в этом треде; merge возможен только после завершения проверки.

Елишев Кирилл and others added 19 commits October 14, 2026 20:57
`await_walk_start` ждал первый файл обхода не дольше 60 секунд по часам. Теперь ожидание
отсчитывает опросы: под нагрузкой каждый опрос растягивается, а бюджет по-прежнему истекает
только тогда, когда обход не начался вообще.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…рки порядка вместо часов

Тесты падали под нагрузкой процессора, потому что решали успех по часам: дедлайн в
секундах у ожидания состояния, порог в 500 мс у статуса, которому нужен лок, пауза
«для прогрева» перед гонкой. Продуктовое поведение не меняется; `McpServer::shutdown`
сохраняет пятисекундное ожидание владельцев, добавлен только `shutdown_within`.

Ожидания состояний:
- `eventually` считает опросы (6000 по 10 мс), а не время; `eventually_within` для ожидания,
  которое задаётся расписанием повторов (бутстрап, 30 с + 60 с).
- Рукописные циклы с дедлайном в тестовом коде переведены на `Patience` (счётчик опросов).
  В тестовом коде таких циклов не осталось.
- `wait_until_watching`, `poll_now`, `tick_now` больше не принимают таймаут: ожидание
  ограничено общим `STATE_CEILING` (60 с), он защищает от зависания, а не меряет скорость.
- Позитивные `recv_timeout`/`timeout` на события с пределами 5–30 с подняты до общего предела.
  Окна отсутствия (`!recv_timeout(100 мс)`) оставлены: они могут скрыть баг, но не вызвать
  ложный отказ.
- `graph/test_support`: `wait_until_within` и `hold_every_handle` считают опросы.

Владельцы и фикстуры:
- `tests/tasks.rs`, `tests/task_deadline.rs`: сервер останавливается и ждёт выхода фоновых
  владельцев до удаления стенда (`Retired`, `shutdown_within`). Раньше фоновая очистка
  `bsl-overlay-backlog` читала уже удалённые `Толпа*`.
- `tests/task_deadline.rs`: перед гейтируемой задачей индекс прогревается, чтобы срок
  попадал в фазу чтения, а не в фазу построения.
- `tests/diagnostics_baseline.rs`: `partial` проверяется после того, как рабочее пространство
  сообщило, что видит оба модуля (`files_total == 2`).

Гонки:
- lease: `hold_file_lock_for_test` ждёт освобождения держателя, а не считает, что lock свободен.
- `all_status_requests_are_cached_under_held_lease`: вместо порога в 500 мс проверяется
  журнал дисковых проверок (`disk_checks_on_this_thread`): статус не ходил за lease.
- `embedding_fence_*`, `an_embed_pass_in_its_retry_pause_is_not_live_work`: выход пасса
  ждётся по счётчику `OwnerStop::live`, держатель отпускается после второй попытки fence.
- `graph::*` (четыре теста из прогона под нагрузкой): `wait_settled` ждёт не только статус
  `Ready`, но и проход публикации, пустой слот сборки и отсутствие обхода (`walk_out`).
- Три теста без `WALK_GATE` читали общий счётчик обходов: теперь держат гейт.
- `a_cancelled_call_frees_the_resident_slot`, `cancelling_a_working_handle_...`: порог
  «не дольше 3 с» заменён сравнением с тем же ожиданием за незавершённым обходом.

Проверки скорости (было 41 `elapsed`-порог в тестовом коде):
- Неблокировка доказывается удержанием ресурса до ответа или журналом дисковых проверок
  (`snapshot`, `hybrid`, `state/mod`).
- Ответ сам доказывает результат: `Pending`/`Failed` вместо «быстрее 500 мс».
- Остались нижние границы (`>= floor`, `>= 200 мс`), они не нарушаются нагрузкой, и
  порядка величины 2× (15/30 с, 30/60 с, 300/600 с) — указано в тестах.

Проверено: `cargo fmt --check`, `cargo clippy -p mcp-server --all-targets --all-features
-D warnings`, `cargo test -p mcp-server --lib` и все интеграционные тесты без нагрузки
проходят; с нагрузкой результаты — в описании PR.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
- Patience считает шаг в наносекундах: шаг в микросекунды больше не усекается до миллисекунды.
- Циклы, которые крутились на `yield_now`, теперь делают шаг по времени: бюджет опросов
  больше не тратится за миллисекунды (сборка графа, тикер тестов сессии, перенос подписки,
  `yield_until` в tasks).
- Смоук-тест индексации снова ограничен своим бюджетом (150 с), а не общим пределом в 60 с.
- Тест shutdown базового сервиса ограничивает возврат десятью производственными
  ack-таймаутами: блокировку по-прежнему доказывает шлюз, а задержку — верхняя граница.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Бюджет ограничивает только ожидание владельцев. Остановка reference worker, baseline и
sweeper выполняется до него без изменений, как и в `shutdown`; документация теперь говорит
это прямо.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
- Watchdog изолированного фикстурного процесса снова отмеряет часами: это бюджет приёмки
  (150 с и 30 с), а не ожидание состояния, и сообщение называет секунды.
- `walk_out` перенесён выше документа `reserve_probe`, чтобы документация не съехала.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
- Проверки задержки ответа на отмену возвращены: `CANCEL_BOUND` (500 мс) как явный контракт
  скорости в `cancel_tests`, его проверят повторные прогоны под нагрузкой.
- Потолки 120 с (resident) и 90 с (владельцы графа) возвращены к прежним значениям:
  сокращение до 60 с ужесточало тесты там, где ожидание самое длинное.
- Контроль `a_cancelled_call_frees_the_resident_slot` меряет холостой вызов на свежем
  резиденте, который обход не трогал.
- Условные ожидания в гейтах `PollGate` и `AnnounceBarrier` списывают бюджет только на таймаут
  (общий шаг `wait_step`), а пробуждения ограничены отдельным пределом.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…аний)

- Ожидание занятости lifecycle в lease-тесте спит между опросами, а не крутится на yield.
- Шаг опроса в metadata равен реальной паузе цикла (100 мс): бюджет 60 с, а не 600 с.
- Отрицательный контроль владельца снова ждёт 2 с: окно отсутствия не сжимается.
- `connect_within` использует свой бюджет (раньше параметр игнорировался).
- Бюджеты ожидания из базы (180/120/90 с) возвращены в name_dictionary, outline,
  references, location_contract и query_validate.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…онде

Проверка текста не ловит чистую задержку: статус, который ждёт зонда, но возвращает тот же
текст, прошёл бы тест. Возвращена проверка «не дольше 500 мс» как явный контракт скорости
(так же, как для отмены поиска).

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
Ожидания, чьим единственным детектором задержки была граница по времени, снова ограничены
исходными значениями: явные проверки `elapsed` (200/500 мс, 1/2/3/5 с, 100 мс) и короткие
ожидания «быстро» как счётчики опросов с исходным номиналом (100 и 200 опросов по 10 мс).
Контроль `a_cancelled_call_frees_the_resident_slot` снова абсолютный (сосед за живым обходом
ждёт больше 1 с): нагрузка только удлиняет такое ожидание, ложного падения не будет.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
- `disk_checks_on_this_thread` на безымянном потоке сравнивает с той же меткой
  `"<unnamed>"`, что и запись: проверка «на этом потоке» не становится пустой.
- Ожидания по опросам имеют стенной предел-страховку 20 минут: дорогой опрос не растягивает
  путь падения на часы. Успешный путь он не касается.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
- Тест «владелец, которому велено уйти, отвечает Stopping при занятом движке» запускает
  вызовы в отдельном потоке и ждёт ответ не дольше секунды: регрессия даёт падение, а не
  зависание.
- Матрица отмены снова проверяет задержку ответа (500 мс) у каждой ячейки; предел 60 с
  остаётся как страховка от зависания.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
- Бюджет, который не помещается на часах (`shutdown_within(Duration::MAX)`), ждёт срезами по
  часу, а не паникует при сложении `Instant`.
- Комментарий в тесте shutdown называет предел 200 мс тем, чем он является: исходным
  контрактом задержки (100 мс ack-таймаут в тестовой сборке, предел вдвое больше).
- Паника 20-минутного предохранителя в `wait_until` называет предохранитель, а не число опросов.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
… теста

Тело теста с #[tokio::test] выполняется на его собственном потоке, а ветка status отвечает
инлайн, поэтому фенс статуса записан под именем потока теста. Фильтр по рантайм-воркерам его
пропускал: регрессия не ловилась. Теперь считаются фенсы текущего потока. Мутация (фенс в ветке
metadata status) валит тест с «a status call fenced the lease».

Также: предел shutdown-теста 1 с (десятикратный ack-таймаут, как написано в комментарии), а
сообщение probe-теста снова называет фактическую длительность.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…ниченное тело опроса

Ревью круга 487 (линзы перебор) дало два mid-замечания, оба подтверждены по коду.

- OwnerStop::wait_empty с бюджетом, который не помещается в Instant, ждал часовыми срезами,
  но после первого истёкшего среза при живом владельце возвращал false. Теперь такой бюджет
  заканчивается только уходом владельцев; ограниченный бюджет ведёт себя как прежде.
  Цикл вынесен в wait_empty_in_slices(timeout, slice), чтобы тест мог взять срез в 10 мс.
- Тело eventually в state/sync.rs спит 200 мс на попытку, а бюджет считал попытки по 10 мс:
  провал растягивался до 20 минут (HANG_GUARD). Теперь eventually_within(100, …) — около 21 с.

Продуктового поведения это не меняет: shutdown по-прежнему ждёт владельцев пять секунд.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
… первой устарелости

Тест baseline_parity_partial_stale_error_recovery_and_cancellation под нагрузкой падал в 8 из 20
прогонов базы и в 10 из 20 после прошлых правок: ответ уже был stale, но baseline ещё `full`.

Правка файла (drift) наблюдается раньше, чем нечитаемость файла (дыра). Хелпер ждал только
stale и отдавал первый такой ответ, а утверждение требовало state `partial`. Теперь хелпер
(workspace_until_partial_stale) ждёт обоих фактов по счётчику опросов. Дыра, однажды
возникнув, держит partial до лечения, так что ожидание сходится в одну сторону.

В сообщение проверки добавлено тело ответа: падение теперь показывает, что именно наблюдалось.

Co-Authored-By: Claude Haiku 5.5 <noreply@anthropic.com>
…ence

test(mcp-server): ожидания тестов не зависят от нагрузки процессора
…space-analysis

# Conflicts:
#	crates/mcp-server/src/lib.rs
#	crates/test-utils/src/walk_probe.rs
itrous and others added 3 commits October 15, 2026 09:42
…alysis

refactor(workspace): share semantic source updates across LSP and MCP
A lookup that misses the file→root map falls back to the BSL root. That
miss is a DashMap absence, so a later registration — a new salsa input —
left module metadata memoised against the fallback path. Depend on an
epoch read at the miss, and bump it when an insert follows a miss.
@itrous
itrous merged commit 2663eab into itrous:develop Oct 15, 2026
3 of 4 checks passed
@itrous

itrous commented Oct 15, 2026

Copy link
Copy Markdown
Owner

🤖 Автоматическая приёмка завершена: изменения приняты, PR влит коммитом 2663eab.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants