Files
jellybit/openspec/changes/linking-transition-robustness/specs/state-reconciliation/spec.md
T
avandClaude Opus 4.8 9bab7dc402 Устойчивость раскладки и переходов linking (MAJOR-4, MINOR-7)
Закрывает две связанные дыры «claim-then-side-effect» в раскладке хардлинками.

MINOR-7: transition глотал ошибку записи состояния — на путях Apply и
авто-раскладки выполнение продолжалось к хардлинкам при незакоммиченном claim
перехода в linking, а финальный linking→done отклонялся графом (задача застревала
со stale-планом). Выделен transitionErr, возвращающий ошибку; Apply и
finishRecognition прерываются ДО linkPlan при провале claim. Обёртка transition
(void) сохранена для fire-and-forget переходов — соседние функции воркера не
тронуты.

MAJOR-4: (A) провал CreateFileLinks после создания хардлинков больше не оставляет
задачу в linking голым return — уводим в review (код persist), повтор Apply
идемпотентен. (B) новый шаг pollOnce sweepLinking возвращает осиротевшие после
краха linking-задачи в review (код interrupted) на тике и старте; любая linking
под w.mu устарела по построению. Восстановлен инвариант «у каждого нетерминального
состояния есть владелец».

Граф переходов не тронут (ребро linking→review уже объявлено). Тесты: провал claim
не создаёт хардлинков; провал учёта уводит в review; sweep осиротевшего linking.

OpenSpec-change linking-transition-robustness (дельты file-layout,
state-reconciliation).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-08 17:18:28 +03:00

2.5 KiB

ADDED Requirements

Requirement: Восстановление задачи, застрявшей в linking

Система SHALL на каждом тике поллинга и при старте выявлять задачи в состоянии linking и возвращать их в review с причиной «прерванная раскладка» (код interrupted), откуда человек повторит применение (повтор идемпотентен). linking — нетерминальное активное состояние, и у него, как у каждого нетерминального состояния, ДОЛЖЕН быть владелец, продвигающий задачу; иначе краш процесса между переходом в linking и финальным переходом оставил бы задачу без владельца — её не листит ни один штатный шаг (ни поллинг активных, ни распознавание, ни матрица сверки, ни восстановление failed/stuck).

Выявление SHALL выполняться под той же блокировкой переходов, что и раскладка: активная раскладка удерживает блокировку весь свой срок и завершает переход из linking до её отпускания, поэтому любая linking-задача, наблюдаемая под блокировкой, по построению устарела (осталась после краха) — восстановление НЕ SHALL задевать раскладку в полёте.

Scenario: Осиротевший linking возвращается в review

  • GIVEN задача осталась в linking после краха между claim и финальным переходом
  • WHEN выполняется тик поллинга (или старт сервиса)
  • THEN задача переходит в review с причиной «прерванная раскладка» (код interrupted)
  • AND её можно повторно применить из ревью

Scenario: Прочие состояния sweep не задевает

  • GIVEN задачи в состояниях done и review
  • WHEN выполняется тик поллинга
  • THEN восстановление linking их состояние не меняет