Commit Graph

1070 Commits

Author SHA1 Message Date
Maxim Dolgolyov 604fa7ac0b fix(sidebar): убрать мигание ссылок «Подготовка к экзамену» при отключении
Ссылки exam-prep (/exam-prep/*) скрывались отдельным async-механизмом (/api/exam-prep/
tracks), не входящим в синхронный кэш-CSS → мелькали на долю секунды после обновления.
Теперь hideDisabledFeatures кэширует точные хрефы скрытых треков в localStorage
(ls_examhide), а _applyFeatureCss добавляет их в инъект-CSS синхронно из кэша на ранней
загрузке (до сборки сайдбара). При включении трека он убирается из кэша → снова виден
(re-apply _applyFeatureCss после свежего fetch). hideEmptySidebarGroups перенесён в конец
hideDisabledFeatures (учитывает скрытие exam-prep).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:56:12 +03:00
Maxim Dolgolyov 38f8be9389 feat(features): тумблер «Путеводитель» (/sitemap)
Пункт «Путеводитель» теперь отключается как остальные модули: ключ sitemap в
вайтлист updateFeatures (backend) + тумблер в admin → фичи (GAME_FEATURES) +
/sitemap,/sitemap.html в FEATURE_HREFS (скрытие из сайдбара + редирект при выкл).
По умолчанию ВКЛ (opt-in disable). Пустая группа схлопнётся авто (hideEmptySidebarGroups).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:51:36 +03:00
Maxim Dolgolyov c04a8c2178 fix(sidebar): прятать пустые группы (заголовок без видимых пунктов)
Когда все пункты группы сайдбара скрыты (фичи отключены / teacher-only у ученика),
оставался висеть пустой заголовок-аккордеон (напр. «Практика и игры»). Добавлена
hideEmptySidebarGroups(): по .sb-group проверяет computed-display пунктов .sb-link
в теле и прячет группу, если ни одного видимого. Зовётся синхронно из sidebar.js
после сборки (по кэш-CSS — без мигания) и в конце hideDisabledFeatures (по свежим
данным; re-show при включении).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:49:51 +03:00
Maxim Dolgolyov 83f0ba9c04 fix(features): пустой блок флешкарт, лаба в сайдбаре, мигание (FOUC)
Три проблемы UX отключения модулей:
1) Пустой блок флешкарт на дашборде: виджет #w-flashcard — <div>, а скрытие шло
   только по [href]. Добавлена карта FEATURE_WIDGETS (флешкарты→#w-flashcard) —
   контейнер прячется целиком.
2) Лаборатория не уходила из сайдбара: не было ГЛОБАЛЬНОГО тумблера lab (только
   free-student). Добавлен в whitelist updateFeatures + GAME_FEATURES; map уже знал
   lab→/lab. Теперь выключение скрывает пункт и редиректит со страницы.
3) Мигание выключенных модулей (FOUC): hideDisabledFeatures асинхронный. Теперь
   loadFeatures кэширует /api/features в localStorage, а _applyFeatureCss инъектит
   <style id=ls-feat-hide> синхронно из кэша на ранней загрузке (api.js идёт до
   sidebar.js) — сайдбар/виджеты строятся уже скрытыми. Геймификация: класс
   no-gamification ставится на <html> (раньше body), ls.css правило body.no-gamification
   → .no-gamification (работает и для html, и для body).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:41:11 +03:00
Maxim Dolgolyov d5fbd0168e feat(permissions): +10 прав ролей с энфорсом (Доступ · роли)
Реестр (registry.js) пополнен правами, которыми раньше нельзя было управлять:
• Учитель: classroom.host (онлайн-уроки), livequiz.host (живые викторины),
  simbuilder.use (конструктор симуляций), flashcards.manage (общие колоды).
• Ученик: homework.submit (сдача ДЗ), materials.save («Мои материалы»),
  assistant.use (ИИ-ассистент), games.play (учебные игры),
  flashcards.access / exam.access (доступ к разделам).
Все default=1 → текущее поведение сохранено; админ может выключить по роли/классу/юзеру.

Энфорс на роутах: учительские — requirePermission (роуты уже teacher-only);
ученические на ОБЩИХ роутах (assistant/materials/games/flashcards/exam-prep) —
новый requirePermissionForStudents(key) (учитель/админ проходят всегда, проверка
только ученику — иначе isEnabled=false сломал бы учителя). PERM_DEFAULTS строится
из реестра → фолбэк до сидирования = enabled, никто не блокируется. Группы UI —
существующие (новых ярлыков нет). seedDefaults авто-сидит новые ключи на чтении.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:31:00 +03:00
Maxim Dolgolyov 54be84e74a fix(admin): глобальный мастер-тумблер «Геймификация» в админ-UI
Геймификация (gamification) была только в FS_FEATURES (free-student grid) — UI
позволял выключить её лишь для роли «свободный ученик», а ГЛОБАЛЬНОГО тумблера в
GAME_FEATURES не было, хотя бэкенд флаг feature_gamification_enabled и kill-switch
это поддерживают. Добавлен в GAME_FEATURES как «Геймификация (всё)» — теперь админ
может выключить XP/монеты/ачивки/магазин/стрики/лидерборд глобально из UI.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:08:59 +03:00
Maxim Dolgolyov dc71d7b4d9 fix(gamification): полнота kill-switch — испытания/стрик/монеты + гейт счётчиков
Аудит выключателя геймификации выявил элементы, НЕ покрытые body.no-gamification:
испытания недели (#ch-section/.ch-widget), календарь стриков (.streak-cal),
стат-кольцо стрика (#sr-streak), монеты в профиле (#p-coins-row), чипы стрик/цель
на карточке питомца. Добавлены в CSS kill-switch (ls.css). Бэкенд: updateChallenges
и onLabExperiment писали прогресс/счётчики без проверки флага — добавлен гейт
isGamificationEnabled() (XP/coins/achievements уже гейтились в award*-функциях).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 17:04:30 +03:00
Maxim Dolgolyov d8f2a7f98d fix(features): доска уходит из сайдбара при отключении + тумблер «Теория»
1) Доска при feature_board_enabled=0 не пропадала у учителя/админа: showBoardIfAllowed()
   зовётся напрямую на ~20 страницах и показывала доску БЕЗ проверки флага, перекрывая
   hideDisabledFeatures(). Теперь функция сперва грузит features и при board===false
   держит ссылку скрытой (для всех ролей).
2) Добавлен фич-флаг theory: ключ в вайтлист updateFeatures (backend), тумблер «Теория»
   в admin → games (GAME_FEATURES), и /theory,/theory.html в map hideDisabledFeatures
   (скрытие из сайдбара + редирект с /theory при выключении). По умолчанию ВКЛ (opt-in disable).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 16:56:22 +03:00
Maxim Dolgolyov 9d35aaf673 fix(admin/access): нативные confirm() → стилизованная модалка LS.confirm
Раздел «Доступ · контент» использовал браузерный confirm() («Подтвердите
действие на localhost…») для закрытия доступа у всех классов и копирования.
Заменены все 5 вызовов на LS.confirm (та же модалка, что в остальном админ-
разделе): «Закрыть доступ» (danger) и «Скопировать доступ» (danger:false).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-22 16:48:24 +03:00
Maxim Dolgolyov bd7dd06e47 fix(exam-prep): репаратор рендеринга ctmath — потерянные \ в опциях + <,> в $…$
Root cause: в seed-ах вариантов 101–121 опции писались как mc('$\sqrt..$') в
обычных кавычках вместо R`…` → JS-парсер съедал \s→s, \d→d и т.п., в БД легло
«$sqrt{17}$», «$dfrac{pi}{3}$» (KaTeX рисует «sqrt17», «dfracpi3»). Плюс литеральные
<,> внутри $…$ ломали HTML до KaTeX. Скрипт fix_ctmath_render.js (dry-run/--apply,
идемпотентный): восстанавливает \ перед командами (+нормализует управляющие символы)
в opts_json и заменяет <,>→\lt,\gt внутри $…$ в text/opts/solution. DRY-RUN: 307
строк (143 opts/128 text/157 sol), остаточных багов 0.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:34:59 +03:00
Maxim Dolgolyov f381873c34 fix(exam-prep): список «Варианты» показывает метку (ЦТ-2015…), а не «Вариант N»
variants.js хардкодил «Вариант ${n}» в гриде-пикере, заголовке и подписи кнопки,
игнорируя поле label из listVariants (бэкенд его уже отдаёт через examVariantLabel).
Добавлен хелпер labelOf(n) → подставляет v.label с фолбэком. mock.js дропдаун уже
использовал label — там достаточно перезапуска сервера, чтобы бэкенд отдал метки.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 19:20:45 +03:00
Maxim Dolgolyov dd69c026ec content(ctmath): вариант 121 — ЦТ-2011 (А1–А18 + В1–В12, 30 заданий)
Перенабор Вариант 1 из ЦТ 2011 В1-В10.pdf (тест полный, не только В), все 30
ответов сверены с официальной таблицей (полное совпадение). Уточнения по таблице:
A6 степень 3x+4 (→15·2^3x), A9=(3^-2)^-5→1/9, A7 корень -3, A8=10, A10=10π.
Фигурные A1/A2/B6 реконструированы (B6: y=x²-6x+9 ∩ y=1,25 → 4x₁x₂=31). Все В
числовые. Без авторских ссылок. Дедуп-гейт 0, KaTeX 30/30, DRY-RUN 30/30. Метка 121='ЦТ-2011'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:53:06 +03:00
Maxim Dolgolyov 84625cd72a content(ctmath): вариант 120 — ЦТ-2012 (А1–А18 + В1–В12, 30 заданий)
Перенабор Вариант 1 из ЦТ 2012.pdf, все 30 ответов сверены с официальной
таблицей (полное совпадение). Фигурные A1/A13/B6 реконструированы с явными
данными (A1 — углы 70/40→равнобедренный; B6 — середины сторон→S=4). A15
уточнена по таблице: √(5⁵·20)=250, знаменатель ⁴√10 → 25·⁴√10. Все В числовые.
Без авторских ссылок. Дедуп-гейт 0, KaTeX 30/30, DRY-RUN 30/30. Метка 120='ЦТ-2012'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:42:27 +03:00
Maxim Dolgolyov 0fb16ef85e content(ctmath): вариант 119 — ЦТ-2013 (А1–А18 + В1–В12, 30 заданий)
Перенабор Вариант 1 из ЦТ2013.pdf, все 30 ответов сверены с официальной
таблицей (полное совпадение). Фигурные A2/A3/A6/A16 реконструированы с явными
описаниями (A2 — образующая=AD, A6 — порядок лучей→40°, A16 — сечение 12×6=72).
Все В-задания числовые (long нет). Без авторских ссылок. Дедуп-гейт 0,
KaTeX 30/30, DRY-RUN 30/30. VARIANT_LABEL: 119='ЦТ-2013'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:35:28 +03:00
Maxim Dolgolyov b9a82c326e content(ctmath): вариант 118 — ЦТ-2017 (А1–А18 + В1–В12, 30 заданий)
Перенабор Вариант 1 из CT-2017.pdf, все 30 ответов сверены с официальной
таблицей (полное совпадение). Фигурные A1/A3/A9/A11/A14 реконструированы с
явными числами (A9 — биссектриса, AM/MC=AB/BC→13,8). Без авторских ссылок.
Прогнан через дедуп-гейт (0 совпадений с пулом) + KaTeX-структуру + DRY-RUN 30/30.
VARIANT_LABEL: 118='ЦТ-2017'.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 16:28:06 +03:00
Maxim Dolgolyov 70cf6b3af1 tools(ctmath): check_variant_dups.js — гейт дедупликации перед добавлением варианта
Постоянный read-only инструмент: (1) без аргумента — аудит видимого пула [101;1999]
на внутренние точные дубли; (2) с seed-файлом — сверяет его TASKS с пулом ДО --apply.
Норма текста: теги/латех/пробелы убраны, ЧИСЛА сохранены (параллельные задачи дублями
не считаются). Сейчас пул = 514 задач, 514 уникальных сигнатур, 0 дублей.
Включается в конвейер тиража: новый вариант проверяется этим гейтом перед записью.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 13:31:02 +03:00
Maxim Dolgolyov 59ae4c1dea fix(exam-prep): практика/тренажёр берут только выверенные варианты (дедуп)
Тренажёр-по-темам и практика брали FROM exam_tasks без фильтра по варианту — в пул
попадали год-пачки (variant=год≥2011) и variant=0, которые ДУБЛИРУЮТ выверенные
варианты-пробники (51 дубль чистый↔пачка, 20 через variant=0). Ученик мог получить
одну задачу дважды.

Добавлен фильтр variant BETWEEN MV_LO..MV_HI (тот же [101;1999], что у пикера) во все
7 запросов выборки/счёта задач: practiceRandom, practiceUnsolved, topicTasksUnsolved,
topicTasksAny, listTopicsWithCounts (счётчик подтем), weakBatchTasks, pickRandomByDifficulty
(×2). Хелперы MV_LO/MV_HI (для math9 без диапазона — всё, кроме variant=0).

Результат: практика ctmath = только варианты 101–117 (496 задач, 0 дублей между собой),
год-пачки (714 задач) остаются в БД для возможного будущего, но не показываются.
Обратимо, без удаления данных. Рантайм-проверка: 5 эндпоинтов практики/тем → 200.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 13:29:02 +03:00
Maxim Dolgolyov de41b77ae3 feat(ctmath): вариант 117 — ЦТ-2021 (32 задания, А1-А18 + В1-В14)
Пробник ЦТ по математике 2021, Вариант 1. Формат: 32 задания (А1–А18 + В1–В14), А12/А16 —
с несколькими верными (тип open, ответ цифрами), В2/В3 множ.выбор, В1 на соответствие.
Источник: чистый PDF ЦТ 2021.pdf. ВСЕ 32 ответа решены и сверены с официальной таблицей
(стр.45, столбец Вариант 1) — полное совпадение, включая B9=324, B11=960, B13=460 (пары чисел),
B14=1375 (описанный четырёхугольник, r=60/7). Фигурные A7/A17/B1/B4 реконструированы; B5: в скане
∛(-7) → на деле ∛(-343)=-7 (иначе нецелый), ответ -98. Без авторских ссылок.
VARIANT_LABEL 117 -> 'ЦТ-2021'. DRY-RUN 32/32, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2021_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 12:22:42 +03:00
Maxim Dolgolyov 59c691dcfc feat(ctmath): вариант 116 — ЦТ-2020 (32 задания, формат А1-А20)
Пробник ЦТ по математике 2020, Вариант 1. ⚠️ Новый формат: 32 задания (А1–А20 + В1–В12),
В1 на соответствие, В2 множ.выбор. Машинерия параметризована N_TASKS=32. Источник: чистый
PDF ЦТ 2020.pdf. ВСЕ 32 ответа решены и сверены с официальной таблицей (стр.44, столбец
Вариант 1) — полное совпадение, включая A20=37√13/3, B5=-335, B8=-320, B9=160, B10=577,
B11=-16, B12=336 (сфера через 4 точки куба). Фигурные A9/A11 реконструированы; без авторских
ссылок (политика «все учебники наши»). VARIANT_LABEL 116 -> 'ЦТ-2020'.
DRY-RUN 32/32, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2020_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 12:09:17 +03:00
Maxim Dolgolyov c0af5502bf chore(textbooks): убрать сторонних авторов — все учебники наши (author=LearnSpace)
Политика «все учебники наши»: нигде не упоминаются сторонние авторы.
- Миграции (15 файлов): колонка author → 'LearnSpace'; из описаний убран оборот
  «по учебнику <автор>:»; авторские фамилии вычищены из комментариев. Покрыты
  Арефьева/Пирютко, Казаков, Латотин/Чеботаревский/Горбунова/Цыбулько, Исаченкова,
  Жилко/Маркович/Сокольский, Герасимов/Лобанов.
- HTML: physics_9_ch5 («по канве учебника Исаченковой» → «по учебной программе»),
  physics_11_hub (hdr-sub с авторами → описание курса), mocks-redesign (карточки-авторы → LearnSpace).
- Генераторы gen_phys9_ch.js/gen_phys11_stubs.js — шаблоны без авторов.
- НОВОЕ: update_textbook_authors.js — идемпотентный апдейтер ЖИВОЙ БД (миграции уже
  применены): author→'LearnSpace' у всех 107 учебников + чистка описаний. DRY-RUN по умолч.

⚠️ Живую БД правит ПОЛЬЗОВАТЕЛЬ: node backend/scripts/update_textbook_authors.js --apply
(в БД сейчас author пуст у всех, видимые упоминания были в описаниях «по учебнику …»).
review_geom10/11.js не тронуты — там фамилии как поисковые шаблоны детектора, не атрибуция.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 11:52:06 +03:00
Maxim Dolgolyov fec638135f chore(ctmath): убрать упоминания сторонних авторов из ссылок-учебников
Поле ref в решениях задач (показывается ученику как «Учебник: …») содержало фамилии
авторов чужих учебников (Арефьева, Казаков, Латотин, Герасимов). Заменено на обобщённые
ссылки нашего курса: «Алгебра, 7 класс, гл. 1» и т.п. (фамилии и кавычки-ёлочки убраны).
452 замены в 15 seed_ctmath_*.js. Синтаксис OK, валидация 30/30.
Применённые варианты (112,113) обновятся при повторном --apply (upsert solution_html).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 11:33:25 +03:00
Maxim Dolgolyov 5881787492 feat(ctmath): вариант 115 — ЦТ-2019 (30 заданий)
Пробник ЦТ по математике 2019, Вариант 1 (А1–А18 + В1–В12; В1 на соответствие, В2 множ.выбор)
для трека exam-prep ctmath. Источник: чистый PDF ЦТ 2019.pdf. Все 30 решены и сверены: столбец
Вариант 1 в таблице (стр.45) затемнён, но читаемые ячейки совпали; методы B5/B6/B7/B11/B12
перекрёстно подтверждены на Варианте 10 (его задания на стр.43-44, ответы читаемы → 81/56/-1071/
624/540 ровно по таблице). Тяжёлые: B7=-264 (период+нечётность), B11=288, B12=110 (т.Гульдина).
Фигурные A1/A7/A9 разрешены по столбцу 1 (D/2√34/2,4); B1/B2 — данные текстом.
VARIANT_LABEL 115 -> 'ЦТ-2019'. DRY-RUN 30/30, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2019_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 11:17:46 +03:00
Maxim Dolgolyov 7990b33fd0 feat(ctmath): вариант 114 — ЦТ-2018 (30 заданий)
Пробник ЦТ по математике 2018, Вариант 1 (А1–А18 + В1–В12, В1 на соответствие) для трека
exam-prep ctmath. Источник: чистый PDF ЦТ 2018.pdf. ВСЕ 30 ответов решены и сверены с
официальной таблицей (стр.32, столбец Вариант 1) — полное совпадение, включая B8=-18,
B9=-130 (двугранный угол), B11=32, B12=45 (координатный метод, PT=5/2).
Фигурные/несогласованные (А3 точки, А9 графики→пути, А11 квадраты, В1/В2 функция по узлам,
В8 экв. показательное, В10 множитель (6-x)² по ответу) реконструированы/адаптированы.
VARIANT_LABEL 114 -> 'ЦТ-2018'. DRY-RUN 30/30, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2018_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 11:04:12 +03:00
Maxim Dolgolyov c86d5b9ad4 feat(ctmath): вариант 113 — ЦТ-2016 (30 заданий)
Пробник ЦТ по математике 2016, Вариант 1 (А1–А18 + В1–В12) для трека exam-prep ctmath.
Источник: чистый PDF ЦТ 2016.pdf. ВСЕ 30 ответов решены и сверены с официальной таблицей
(стр.35, столбец Вариант 1) — полное совпадение, включая B5=-22, B9=712, B11=56, B12=724.
Фигурные задания (А2 угол через MN||BC, А3 числа на прямой, А6 таблица, А7 площадь по
координатам, А8 область значений, А11 круговая диаграмма) реконструированы/адаптированы
в самодостаточные авто-проверяемые формы. VARIANT_LABEL 113 -> 'ЦТ-2016'.
DRY-RUN 30/30, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2016_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 10:37:13 +03:00
Maxim Dolgolyov 7e8082bda6 feat(ctmath): вариант 112 — ЦТ-2015 (30 заданий)
Пробник ЦТ по математике 2015, Вариант 1 (А1–А18 + В1–В12) для трека exam-prep ctmath.
Источник: чистый PDF ЦТ 2015.pdf (10 изоморфных вариантов, таблица ответов стр.35).
Ответы решены и сверены: читаемые ячейки столбца В1 (B2=-15,B3=7,B7=147,B8=-6 совпали)
+ изоморфные варианты 2–10. Фигурные задания (А4 центр.симметрия, А6 множество решений,
А11 таблица-данные, А12 парабола, А15 координаты, В11 лог-выражение) адаптированы в
самодостаточные авто-проверяемые формы с сохранением ответа (как в ЦТ-2014/вар.110).
VARIANT_LABEL 112 -> 'ЦТ-2015'. DRY-RUN 30/30, self-check и структурный KaTeX — зелёные.
Запись в БД — пользователь: node backend/scripts/seed_ctmath_ct2015_v1.js --apply

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-20 10:24:59 +03:00
Maxim Dolgolyov 2e9a0ebfb1 feat(panel): обновление из репо, обслуживание БД, авто-прунинг, цветные логи и Сторож
- [U] Обновление из репозитория: бэкап -> git pull --ff-only -> npm install -> миграции
  -> рестарт -> health-check; при провале миграций/health предлагает откат (git reset --hard
  + восстановление БД из свежего бэкапа). Текущая версия (git short-hash + subject) в шапке.
- [M] Обслуживание БД: backend/scripts/db-maintain.js (node:sqlite) — integrity_check ->
  WAL checkpoint(TRUNCATE) -> VACUUM; VACUUM пропускается на битой БД. Авто-бэкап + стоп/старт.
- Авто-прунинг бэкапов: Backup-Db хранит последних 10 (Prune-Backups), Copy-DbFrom вынесен
  общим (реюз в Restore-Db и откате обновления), запоминается путь последнего бэкапа.
- Живые логи: отдельный tools/tail-logs.ps1 — раскраска уровней (ERROR/FATAL красным,
  WARN жёлтым, успех зелёным) вместо сырого tail; вынос из inline-команды (PS 5.1 quoting).
- Экран «Сторож»: дашборд в рамке с перерисовкой — статус-маркер, счётчики проверок/
  перезапусков, последнее событие; выход по клавише.
Все .ps1 — UTF-8 BOM, парсинг OK; db-maintain протестирован на копии БД (10.7->10.5 МБ);
рендер-смоук подтвердил выравнивание.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 23:10:52 +03:00
Maxim Dolgolyov 27f51f1a61 style(panel): дашборд с рамкой, цветной статус-маркер, сгруппированное меню
UI консольной панели:
- Закрытая рамка из box-символов (╔═╗║╠╣╚╝) вместо голых ===; правый бордюр выровнен
  на всех строках (хелперы B-Top/B-Mid/B-Bot/B-Line + B-Status с цветным маркером ●).
- Статус-блок: ● зелёный РАБОТАЕТ / серый ОСТАНОВЛЕН, health цветом по состоянию,
  строка БД (размер · юзеры · миграция-номер · бэкапов), Node/JWT/LLM/время обновления, URL.
- Меню в две выровненные колонки СЕРВЕР | ОБСЛУЖИВАНИЕ (ключи голубые, подписи серые),
  отдельная строка ДИАГНОСТИКА; промпт с ▶.
Чистый рефактор отрисовки — логика switch/функции не тронуты. UTF-8 BOM, парсинг OK,
рендер-смоук показал ровное выравнивание.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 23:00:02 +03:00
Maxim Dolgolyov 6eefb70ce7 feat(panel): бэкап/восстановление БД, умный статус, создать админа, watchdog
control-panel.ps1 расширена:
- Бэкап БД [B] (копия learnspace.db+wal/shm с датой в data/backups) и восстановление [R]
  (выбор из списка, страховочная копия .pre-restore, авто-стоп/старт сервера).
- Умный статус: health-пинг /api/health (+ms), размер БД, кол-во пользователей, последняя
  миграция (db-status.js), версия Node, сводка .env (CLIENT_ORIGIN/JWT/LLM). Кэш в .
- Создать админа [A] → scripts/create-admin.js (bcrypt, upsert role=admin, busy_timeout).
- Сторож [W]: авто-перезапуск при падении (выход по клавише). Логи в backend/logs (не %TEMP%),
  [E] ошибки из логов.
Фиксы PS 5.1: порт/путь БД читаются из .env (inline node -e с кавычками 5.1 ломает); db-status
  вынесен в файл-скрипт; миграция по filename DESC; UTF-8 BOM, парсинг OK. Меню — лат.+рус. клавиши.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 22:40:45 +03:00
Maxim Dolgolyov 047a3a7e15 deploy: compose.truenas.yml + инструкция под SCALE/сборку-на-NAS
- compose.truenas.yml: готовый host-path compose (build на NAS / или image), env
  с JWT_SECRET/CLIENT_ORIGIN, healthcheck start_period 40s (запас на первичные миграции).
- DEPLOY-TRUENAS.md: переписан под реальный кейс (TrueNAS SCALE, сборка образа на NAS,
  без Docker на ПК): датасет → код по SMB → docker build → правка JWT/пути →
  docker compose up / Custom App → проверка → домен/HTTPS/бэкапы. PC-сборка и CORE — в конце.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 20:12:56 +03:00
Maxim Dolgolyov 6c3a3fe982 chore: .gitattributes — *.sh всегда LF (entrypoint в Linux-контейнере)
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 19:54:15 +03:00
Maxim Dolgolyov f7c5f222a3 deploy(docker): self-init entrypoint (миграции+засев прав) + гайд по TrueNAS
- docker-entrypoint.sh: при старте node migrations-runner (идемпотентно) + seed-permissions
  только если role_permissions пуста → контейнер поднимается на чистом томе без ручных шагов
  (сервер раньше fail-fast без миграций). Dockerfile: ENTRYPOINT через tini + entrypoint,
  нормализация CRLF (sed) + chmod, label BQ-System → LearnSpace.
- DEPLOY-TRUENAS.md: пошагово для TrueNAS SCALE (датасет → образ → Custom App compose с host-path
  томами и JWT_SECRET → авто-миграции → reverse-proxy/HTTPS/TURN → бэкапы), заметка про CORE.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 19:53:24 +03:00
Maxim Dolgolyov d63c99cae9 chore(brand): убрать «BQ-System», оставить только LearnSpace
Бренд продукта = LearnSpace. Убрано «BQ-System»/«LearnSpace / BQ-System» из:
- банеров и комментариев запускатора/панели (control-panel/launch-server.ps1, *.bat);
- заголовка CLAUDE.md;
- планов ct-math (PLAN/README).
Путь-каталог (cd BQ-System в SETUP.md, папка на диске) и .claude-настройки — не трогаю
(это локальные пути, не брендинг). ps1 пересохранены в UTF-8 с BOM, парсинг OK.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 17:01:53 +03:00
Maxim Dolgolyov 2d7833cad9 test: зелёный сьют — синхрон политики пароля (8), jsdom devDep, serial-прогон
Чинит 8 «baseline»-падений (теперь 330/330):
- auth (3): контроллер/фронт требуют пароль >=8, а схема роута (minLen:6) и тест
  (7-симв. 'pass123') устарели → схема register/profile 6→8, тест-пароли → 8 симв.
  (login/duplicate падали как следствие незарегистрированного юзера).
- page (5): jsdom не был установлен → добавлен в devDependencies.
- флакость jsdom-страниц при параллельном прогоне (фикс. wait под нагрузкой CPU) →
  npm test с --test-concurrency=1 (детерминированно; в изоляции тесты и так проходят).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 16:53:04 +03:00
Maxim Dolgolyov eed8343977 chore(tools): панель управления сервером + stop-server.bat
control-panel.bat → tools/control-panel.ps1: меню-консоль (статус с PID/портом/
аптаймом, старт/стоп/перезапуск, живые логи в отдельном окне, миграции, npm test,
lint:routes, открыть сайт). Сервер запускается в фоне (Start-Process hidden, логи в
%TEMP%) и переживает закрытие панели. stop-server.bat → control-panel.ps1 -Stop.
ps1 в UTF-8 с BOM. Проверено: -Start при работающем сервере → «уже работает».

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 16:33:53 +03:00
Maxim Dolgolyov c7ef5c0448 chore(tools): консоль-запускатор сервера (start-server.bat + tools/launch-server.ps1)
Двойной клик по start-server.bat → окно-консоль с шапкой (URL/порт/режим/статус),
авто-применением миграций (идемпотентно), освобождением порта от старого экземпляра,
живыми логами и меню перезапуска при остановке/падении. Флаг -Dev → nodemon
(авто-перезапуск при правках кода), -NoMigrate → без миграций.
ps1 в UTF-8 с BOM (корректная кириллица в PowerShell 5.1).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 16:25:15 +03:00
Maxim Dolgolyov 82d323547f feat(prep): тумблер «готовится к ЦТ» на странице персональных учеников
my-students.html: колонка «ЦТ» с тумблером у каждого ученика (teacher_students вне
классов). Бэкенд уже поддерживал (canManageStudent включает teacher_students);
статус грузится per-student через LS.prepStudentTracks, переключение —
prepSetStudent/prepUnsetStudent. togglePrep на window (скрипт-модуль). Иконки — SVG.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 16:06:41 +03:00
Maxim Dolgolyov 4aacb2d369 feat(prep): фронтенд мастер-флага ЦТ — папка-коллекция карточек + тумблер у учителя
- flashcards.html: колоды коллекции рендерятся сворачиваемой папкой «Подготовка к ЦТ»
  (deckCardHtml вынесен, секции <details> по collection; метки из LS.prepListTracks)
- classes.html: в таблице учеников колонка «ЦТ» с тумблером флага + кнопки «Весь класс → ЦТ»/
  «Снять ЦТ» (LS.prepClassStatus/prepSetStudent/prepUnsetStudent/prepSetClass)
Иконки — inline SVG, без эмодзи.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 15:37:36 +03:00
Maxim Dolgolyov 9509a67e25 feat(prep): мастер-флаг подготовки к направлению (ЦТ) + коллекции колод — бэкенд
Система «готовится к ЦТ»: флаг student_prep(user_id,track) открывает ученику
ВЕСЬ контент трека (карточки + курс + пробники) динамически, без материализации.
- мигр.078: таблица student_prep + flashcard_decks.collection + разметка ЦТ-колод 'ct-math'
- services/prepTracks.js: реестр треков (трек→коллекция/курсы/экзамены), устойчив до миграции
- contentAccess.resolve/allowedRefs: учитывают мастер-флаг (явный запрет ученика побеждает)
- flashcardController.deckAccess/listDecks: колоды коллекции открыты по флагу
- prepController + /api/prep: учитель (своим) и админ ставят/снимают флаг (ученику/классу)
- js/api.js: LS.prep* обёртки

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 15:29:00 +03:00
Maxim Dolgolyov 5193fd8252 feat(ctmath): пробник ЦЭ-2024 Вариант 1 (вариант 111)
Актуальный формат экзамена: А1–А10 (8 mc + 2 open) + В1–В20 (1 long + 19 open).
Перенабор по PDF сборника РИКЗ; решений в источнике нет — решено вручную,
ВСЕ 30 ответов сверены с официальным ключом (стр.35, столбец Вариант 1).
Адаптации: А1 (точки на прямой) → равные промежутки в тексте; А6 (промежуток
на рисунке) → описан словами. Метка 111 (ЦЭ-2024) в VARIANT_LABEL.
Идемпотентный seed, --apply — пользователь.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 12:50:47 +03:00
Maxim Dolgolyov f4d20ff10f feat(ctmath): пробник ЦТ-2014 Вариант 1 (вариант 110)
Первый из ЦТ-годовых. Формат ЦТ: А1–А18 (18 mc) + В1–В12 (12 open) = 30.
Перенабор по PDF; решений в источнике НЕТ — решено вручную, ответы
сверены с официальным ключом (стр.34 сборника). Адаптации картинок:
А2 (симметричные фигуры, неразборчивы) → MC о симметрии точки; А6
(параллелограмм на сетке) → координаты вершин; А10/А15 — текстом.
Метка 110 (ЦТ-2014) в VARIANT_LABEL. Идемпотентный seed, --apply — пользователь.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 12:32:53 +03:00
Maxim Dolgolyov f856f84de0 feat(ctmath): пробник РТ-2022/23 этап III (вариант 109)
30 заданий А1–А10 + В1–В20, перенабор по PDF РИКЗ.
8 mc + 19 open + 3 long. Геометрия — текстом, А6 (чтение графика)
— inline-SVG в figure_html (кусочно-линейная функция, все 5
утверждений и ответ 134 согласованы). Метка 109 уже в
VARIANT_LABEL. Идемпотентный seed, --apply — пользователь.
Завершает набор РТ-2022/23 (107/108/109).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:07:33 +03:00
Maxim Dolgolyov c0dd8ba698 feat(ctmath): пробник РТ-2022/23 этап II (вариант 108)
30 заданий А1–А10 + В1–В20, перенабор по PDF РИКЗ.
8 mc + 20 open + 2 long. Геометрия — текстом. Адаптации заданий
с картинкой: А1 (термометр) → показание числом; А3 (выбор
прямоугольника) → MC о соотношении сторон; А6 (графики) → список
функций (ответ 145); В1 (диаграмма) → данные таблицей в
figure_html (ответ А6Б4В3). Метка 108 уже в VARIANT_LABEL.
Идемпотентный seed, --apply — пользователь.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 11:00:31 +03:00
Maxim Dolgolyov d2d379c5f5 feat(ctmath): пробник РТ-2022/23 этап I (вариант 107)
30 заданий А1–А10 + В1–В20, перенабор по PDF РИКЗ.
8 mc + 20 open + 2 long. Геометрия — текстом. Адаптации заданий
с картинкой: А5 (выбор графика) → MC о параболе y=x²−4; В1
(промежуток↔рисунок) → сопоставление со словесными описаниями
(ответ А3Б6В2); В3 (диаграмма) → данные таблицей в figure_html
(ответ А1Б2В6). Метки 107/108/109 (РТ-2022/23) в VARIANT_LABEL.
Идемпотентный seed, --apply — пользователь.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:52:54 +03:00
Maxim Dolgolyov 494023fba7 feat(ctmath): пробник РТ-2023/24 этап III (вариант 106)
30 заданий А1–А10 + В1–В20, перенабор по PDF РИКЗ.
8 mc + 21 open + 1 long; геометрия — текстом, В1 (чтение
графика) — inline-SVG в figure_html (как у math9). Метка 106
уже в VARIANT_LABEL. Идемпотентный seed, --apply — пользователь.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:25:10 +03:00
Maxim Dolgolyov ddb49cf0c1 feat(ctmath): пробник РТ-2023/24 этап II (вариант 105)
30 заданий А1–А10 + В1–В20, перенабор по PDF РИКЗ.
8 mc + 21 open + 1 long; геометрия закодирована текстом.
Идемпотентный seed (upsert), DRY-RUN по умолчанию. Метка 105
уже в VARIANT_LABEL. Запуск с --apply — пользователь.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:17:36 +03:00
Maxim Dolgolyov fd656ed63f feat(ctmath): скрипт открытия ЦТ-математики классу (publish курса 13 + доступ)
Идемпотентно: courses.is_published=1 (курс 13) + content_access classу #4
«10Б · Математика» на курс (course:13) и экзамен-модуль (exam:ctmath).
Модель — allowlist (без правил ученики не видят даже опубликованный курс).
Цель класса флагом --class=<id> (деф. 4), сверка имени. DRY-RUN по умолчанию,
запись с --apply (outward-facing, запускает пользователь).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 10:06:51 +03:00
Maxim Dolgolyov 17c1c92490 feat(ctmath): эталонный вариант-пробник РТ-2023/24 Этап I (variant 104)
30 заданий (А1–А10 + В1–В20), перенабрано вручную в KaTeX по PDF РИКЗ
(РТ-1 23/24 В1). Геометрия закодирована текстом — чертежи не нужны.
Идемпотентный upsert, DRY-RUN по умолчанию, запись с --apply.
Верификация: node --check, валидация 30/30, KaTeX-рендер 413/413 сегментов.
+ метки вариантов 104–106 (РТ-2023/24 этап I/II/III) в routes/exam-prep.js.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 09:47:44 +03:00
Maxim Dolgolyov 824ca369bb feat(ctmath): большой батч флешкарт — 8 колод по оставшимся темам ЦТ
ФСУ (8), Иррациональные уравнения (7), Показательные ур./нерав. (7),
Логарифмические ур./нерав. (8), Метод интервалов (6), Вектора на плоскости (10),
Теория вероятностей и комбинаторика (9), Параметры (6) — итого 61 карта.
Канонический материал ЦТ. KaTeX inline $…$. Самопроверка усилена: парность $/{}
+ запрет кириллицы внутри $…$ (math-режим KaTeX). Идемпотентно, запись с --apply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 09:33:39 +03:00
Maxim Dolgolyov 70ec09382e feat(ctmath): seed-скрипт колод флешкарт «Квадратные уравнения» и «Модуль»
Квадратные уравнения (12 карт) — дискриминант, формула корней, теорема Виета
(вкл. приведённое), неполные уравнения, разложение на множители, знаки корней.
Модуль (12 карт) — определение, геометрический смысл, уравнения |x|=a / |f|=|g| /
|f|=g, неравенства |x|<a / |x|>a, свойства, раскрытие по промежуткам.
Канонический материал ЦТ. KaTeX inline $…$ (кириллица только вне math),
идемпотентно, запись с --apply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 09:27:25 +03:00
Maxim Dolgolyov 2bdb0ed898 feat(ctmath): seed-скрипт колод флешкарт «Системы уравнений» и «Текстовые задачи»
Системы (7 карт) — методы подстановки/сложения, домножение коэффициентов,
пересечение графиков = система, проверка пары, приём x²−y²=(x+y)(x−y)
(источник: Кедр «Материал по системам»). Текстовые задачи (12 карт) —
проценты, сплавы/растворы, движение, совместная работа (канонические приёмы).
KaTeX inline $…$ (кириллица только вне math), идемпотентно, запись с --apply.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-19 09:12:39 +03:00