Хэндофф: spec-059 «Заклинания и свитки» — автономная сборка

Для: новой сессии Claude, собирающей фичу целиком, пока Andrey спит. Модель работы: trust (без пред-одобрений на код), spec-driven, агент- оркестрация (2–3 исполнителя + твоя интеграция). Внешние действия (push/PR/deploy) — только по явному «да» Andrey. Язык — русский.


TL;DR

Andrey хочет: (1) базу ВСЕХ официальных заклинаний dnd.su (2 редакции), нода spell с переключателем 2014/2024; (2) четыре новых глагола экономики: написать свиток, переподготовить заклинание (наша house-механика), переписать свиток → книгу и книгу → книгу (RAW-правила волшебника). Сейчас: спека готова, разведка dnd.su готова, код не начат. В /tg этих экранов нет, нод заклинаний в каталоге нет — это и есть работа.

Старт (порядок чтения)

  1. spec.md (в этой папке) — полные правила, таблица цен, 4 этапа, критерии.
  2. dndsu-recon.md (в этой папке) — как устроен dnd.su/spells (закрывает Open #2: 2014=dnd.su, 2024=next.dnd.su, пары по slug, homebrew мимо).
  3. Прогони spec-driven: спека → plan → tasks → код → ревью (папка .specify/specs/ — прецеденты 056/058; учти: .specify на этой машине в рабочее дерево не материализуется — держи рабочие артефакты в git- трекаемом месте, эта папка docs/features/scrolls-and-spells/ надёжна).
  4. Бут-контекст репо — MEMORY.md Claude + AGENTS.md (правила проекта).

Ветка и фундамент

  • Ты на claude/spec-059-build, отпочкована от свежего origin/main — весь фундамент на месте: крафт (056: app/actions/craft.ts, lib/craft-settings.ts, lib/queries/craft-tg.ts, party_level петли), /tg-хаб глаголов (058: app/tg/_components/action-sheets.tsx + action-hub.tsx), общак/склад.
  • Старая claude/spec-059-scrolls (только коммит спеки, отставала на 8) — не нужна, её спека восстановлена здесь в spec.md. Можно удалить.

Порядок сборки

Этап 1 — база заклинаний (фундамент, делать первым):

  • Форк скрапера: scripts/scrape_dndsu.py (кэш/fetch/html→md) + scripts/items-dndsu-codegen.ts (codegen сид-миграций) — прецедент предметов spec-018. Discover по /spells/ на dnd.su И next.dnd.su.
  • Миграция node_type = 'spell' + поля (level/school/…/slug + markdown 2014 и 2024-nullable). Вики-рендер ноды (десктоп + /tg) с переключателем редакции.

Этапы 2/3/4 — глаголы (независимы, параллельно поверх базы):

  • 2 «Свиток»: scribe_settings (паттерн craft-settings.ts), runScribe (канон runCraft), /tg-sheet в табе 🏰 Партия рядом с Крафтом.
  • 3 «Переподготовка»: house-механика, 50×уровень, мгновенно, глагол хаба.
  • 4 «Копирование»: свиток→книга (расход свитка) и книга→книга (нарратив), 50×уровень + 2ч×уровень, RAW.
  • Все три — новые глаголы spec-058-хаба (action-hub.tsx / спелл-контекст).

Критические грабли (набито этой сессией — не наступи)

  1. PostgREST embed-ambiguity = молчаливый пустой каталог. Второй FK на nodes из дочерней таблицы (у нас item_attributes.schema_for_node_id) сделал ВСЕ эмбеды nodes → child!inner(...) неоднозначными → пустые каталог/покупка/крафт, БЕЗ ошибки (код делал const { data }, глотая error). Если связываешь spell/scroll с нодами через FK — не плоди второй FK на nodes из одной таблицы; предпочитай soft-link (uuid-колонка без FK) + отдельный резолв-запрос. После DDL-смены схемы обязательно notify pgrst, 'reload schema' (иначе PostgREST держит старую топологию). См. MEMORY.md → postgrest-embed-fk-ambiguity.
  2. Числа механики = ДМ-настройки, НЕ хардкод (AGENTS.md). Таблица цен свитков, 50×уровень, 2ч×уровень, hoursPerDay/Week, maxSpellLevel- формула(правило, не настройка) → scribe_settings/spell_settings в campaigns.settings, parse-хелпер с дефолтами (паттерн parseCraftSettings). Числа из спеки = дефолты парсера.
  3. Read-хелперы должны падать ГРОМКО. const { data } = await … глотает ошибку → пустой список маскирует сбой под «нет данных» (потеряли час на пустых схемах). Бери { data, error } и if (error) throw error — экран ловит и показывает ошибку.
  4. Sidebar cache (AGENTS.md): вставка/правка node_types/nodes (новые типы spell/scroll, сид спеллов) трогает nodes → инвалидируй (invalidateSidebar/invalidateSidebarRemote для CLI-сида).
  5. Накат миграций (MEMORY.md → db-migration-apply): сам катишь на прод — ssh andrey@37.27.254.49 "docker exec -i supabase-db psql -U supabase_admin -d postgres -v ON_ERROR_STOP=1" < …/NNN.sql. Verify-блок обязателен (стиль 124–129). После schema-DDL — reload PostgREST (см. грабли №1).
  6. /tg нельзя превьюить в браузере (нужна Telegram initData). Верификация: npm run typecheck + npm run build + vitest + агент ux-auditor (обязателен после любой /tg UI-правки и перед PR). Плюс ручной смоук Andrey.
  7. party_level уже = 9 (задано этой сессией). maxSpellLevel(9)=5-й круг.

Открытые решения (взяты дефолты — помечай при несогласии)

Из spec.md → Open: деньги переподготовки/копирования — с кошелька PC (дефолт) vs общак; событие ленты — mode 'scribe'/'reprep'/'copy' в типе 'craft' vs отдельные типы (реши в Plan по чистоте union); связь свиток→спелл — soft-link uuid vs fields (см. грабли №1 — избегай второго FK на nodes). Заговор при переподготовке = 0 зм (бесплатно). Эти форки не блокируют сборку — бери дефолт, помечай в коде, Andrey отревьюит.

Связанное (не дубли)

  • 3 tg-фикса ждут PR на ветке claude/fix-tg-portrait (портрет при смене PC; listSchemas громкая ошибка; взятие из общака по получателю + остаток в TakeSheet). Не переделывай — они готовы, ждут «да» Andrey на push/PR.
  • Вопрос Andrey (в бэклог): кнопка «Mother of Learning» в супергруппе как в личке. Ответ Claude: постоянной menu-button (у поля ввода) в группах Telegram нет — это только приват. Реалистично: закреплённое сообщение с inline web_app-кнопкой ИЛИ Direct-Link Mini App t.me/<bot>/<app> в закрепе/описании группы. Attachment-menu-бот — только для крупных ботов через аппрув Telegram. Оформить как мелкую задачу, если Andrey подтвердит.