Files
Learn_System/plans/sim-builder/CONTEXT.md
T

13 KiB
Raw Blame History

Feature Context: Конструктор симуляций (SimForge)

Current State

  • Фаза 3 РЕАЛИЗОВАНА (в рабочем дереве, не закоммичено — коммит за оркестратором). Только backend + клиент js/api.js (lab.html/lab-glue.js НЕ тронуты — зона параллельной сессии).
    • Миграция 071 backend/src/db/migrations/071_custom_sims.sql — таблица custom_sims (применена к живой БД через npm run migrate, без ошибок).
    • API /api/custom-sims (роутер backend/src/routes/customSims.js, контроллер backend/src/controllers/customSimController.js, смонтировано в server.js): GET / (свои+published), GET /:id (own ИЛИ published), POST / (teacher/admin), PUT /:id (owner/admin), DELETE /:id (owner/admin). Read — router-level authMiddleware; мутации — inline requireRole + per-row ownership.
    • validateSpec(spec) в контроллере — серверная валидация БЕЗ исполнения: ≤200KB, specVersion=1, лимиты (params≤50/objects≤200/walls≤20/springs≤50/expr≤500/глубина≤8/points≤1000), whitelist типов объектов, physics (restitution 0..1, dt 1/2000..1/30, mass>0), санитизация текст-полей (escape &<>). Возврат { ok, error?, clean? }.
    • Клиент js/api.js: customSimsList/Get/Create/Update/Deletereq(...), добавлены в window.LS.
    • Верификация: node --check всех новых/изменённых .js OK; npm run migrate OK; npm run lint:routes чисто (0 unprotected, baseline 0); backend/tests/custom-sims.test.js 24/24 pass; общий suite 201/209 (8 fail = 3 baseline auth.test.js + 5 page-тестов без devDep jsdom — окружение, не моя фаза). Эмодзи нет; БД через node:sqlite.
  • Фаза 2 РЕАЛИЗОВАНА (в рабочем дереве, не закоммичено — коммит за оркестратором). Только _sim_engine.js + _sim_demo.js (lab.html/lab-glue.js НЕ тронуты — зона параллельной сессии).
    • Физический режим: блок physics:{ enabled, gravity:{x,y}, friction?, restitution?, dt?, walls?:[...], springs?:[...] } + body:{ mass, vx, vy, fixed } на point/circle. Фикс-шаговый полу-неявный Эйлер (накопитель dt, кламп шага/скорости), опора на математику _fx_motion.spring. Упругие столкновения круг-круг и круг-стена (restitution), пружины (Гук+демпф) между телами/якорями. Drag тел (тащишь — позиция, отпускаешь — бросок со скоростью). Тела сосуществуют с формульными объектами Ф0/Ф1.
    • env-поля тел: <id>.x/.y/.vx/.vy берутся из СОСТОЯНИЯ интегратора и кладутся в env первыми — снимает forward-ref проблему однопроходного env для тел.
    • Интегратор экспортирован как window.SimPhysics (для билдера/доски/headless). Отдельного файла _sim_physics.js НЕТ (нельзя подключить без правки lab.html — зона параллельной сессии); код внутри _sim_engine.js.
    • Демо за флагом: +customphys (пружинный маятник), +customballs (упругие шары). Гочи: имя param e зарезервировано (число Эйлера) — в демо «шары» упругость названа el.
    • Верификация: node --check обоих файлов OK; eval/Function — только в комментарии; эмодзи нет (скан кодпойнтов); headless (vm+DOM/canvas-стаб) 28/28: падение под гравитацией (парабола, без NaN), упругие шары (скорости меняются, тела в коробке, ограничены), пружинный маятник (колебания, без взрыва), drag тела (позиция+бросок), смешанная сцена (формульный point + segment на ball.x/y + readout ball.y живут вместе), SimPhysics.step raw.
  • Фаза 1 РЕАЛИЗОВАНА (в рабочем дереве, не закоммичено — коммит за оркестратором). Только _sim_engine.js + _sim_demo.js (lab.html/lab-glue.js НЕ тронуты — зона параллельной сессии).
    • Новые типы объектов спеки: plot (график f(var) на canvas движка, trace — след по t), readout (живой бейдж, мягкая ошибка через evalSafe), vector с формой origin+dx/dy. drag на point/circle (drag:{param,axis,min,max,paramY}) — pointer events (мышь+тач), хит-тест в px (16px), двойной clamp (drag.min/max + диапазон параметра). Точные поля — в шапке _sim_engine.js и handoff phase-1.
    • Демо customdemo расширено: +слайдеры x0/y0, draggable-старт (axis xy), plot траектории, 2 readout (R, H). По-прежнему за флагом.
    • Верификация: node --check обоих файлов OK; eval/Function — только в комментарии, ни одного call-site; эмодзи нет (скан кодпойнтов); headless-тесты (vm + DOM-стаб): подготовка типов, vector end=origin+(dx,dy), plot evaluate, readout evalSafe, drag clamp+slider-sync, рендер всех 8 типов демо ×6 кадров без ошибок, trail/readout-слоты накапливаются корректно.
  • Фаза 0 РЕАЛИЗОВАНА (в рабочем дереве, не закоммичено — коммит за оркестратором). Ветка feature/sim-builder от master.
    • frontend/js/labs/_sim_expr.jswindow.SimExpr (безопасный движок выражений, без eval/Function; compile/evaluate/evalSafe/compileValue/parse/tokenize, whitelist + сравнения/логика/тернарник/multi-var env).
    • frontend/js/labs/_sim_engine.jswindow.SimEngine.mount(host, spec) -> { play, pause, reset, setParam, getParam, isRunning, destroy, el }. Canvas (мир→экран, Y вверх) + KaTeX-оверлей подписей + слайдеры/play/pause/reset. Формат спеки v1 задокументирован в шапке файла.
    • frontend/js/labs/_sim_adapter.jswindow.registerSpecSim(spec) / window.SimAdapter — строит манифест LabRegistry (ленивый хост #sim-spec-host-<id> в #lab-sim).
    • frontend/js/labs/_sim_demo.js — демо customdemo (бросок тела) за флагом ?simdemo=1 / ?sim=customdemo / LAB_SHOW_SPEC_DEMO / localStorage lab-spec-demo=1. Ученикам не светится.
    • Подключение в frontend/lab.html: 3 каркасных модуля eager после _graph_panel.js, демо после _register-all.js. _sim_deps.js НЕ тронут.
    • Верификация: node --check все 4 файла OK; eval/Function отсутствуют (только в комментариях); эмодзи нет; SimExpr self-test 29/30 (единственный «FAIL» -2^2=4 — это парити с graph.js).
  • Лаборатория уже декларативна на уровне регистрации: frontend/js/labs/_registry.js (LabRegistry.register/get/all/setActive/stop/destroy/resolvePreview), манифест с open(ctx)/mount(host)/stop/destroy. ~40 симуляций — рукописные JS-модули в frontend/js/labs/.
  • Каталог в БД: миграция 042_lab_sims.sql (lab_sims), роуты backend/src/routes/lab.js (GET /api/lab/sims, PATCH/:id, POST /reorder, links). Привязка к программе: 043_lab_sim_links.sql.

Архитектурные решения (зафиксированы при планировании)

  • Спека = JSON-данные. Версия specVersion. Корень: { specVersion, meta, viewport, params[], objects[], physics?, plots[], controls }.
  • Движок выражений безопасный — собственный парсер (расширение y=f(x) из graph.js): токенайзер → AST → eval по окружению { params, t, объекты, whitelisted Math fns }. Без eval/Function. Whitelist: + - * / ^ %, sin cos tan asin acos atan sqrt abs exp ln log min max floor ceil round sign pi e, сравнения, ?:.
  • Рантайм window.SimEngine.mount(host, spec) -> instance{ play, pause, reset, setParam, destroy }. Рендер: canvas для геометрии/трасс + SVG/absolute-div оверлей для подписей (KaTeX). Регистрируется в LabRegistry адаптером (одна функция строит манифест из спеки).
  • Объект: { id, type, ...props-with-bindings }. type ∈ point|segment|vector|circle|rect|polyline|path|label|image. Любое числовое свойство может быть числом ИЛИ строкой-выражением.
  • Физический режим (Фаза 2): объект с body:{ mass, vx, vy, fixed } интегрируется _fx_motion; силы physics:{ gravity, springs[], collisions, friction, walls }. Формульный и физический режимы сосуществуют (формульные объекты — кинематические).
  • Безопасность шаринга: published-спека валидируется на сервере (размер, схема, глубина AST, число объектов/параметров); подписи-строки санитизируются как svg/текст.

Temporary Workarounds

  • (нет)

Cross-Phase Dependencies

  • Ф1 (графики/drag) зависит от рантайма Ф0.
  • Ф2 (физика) зависит от Ф0 (модель объектов/цикл).
  • Ф4 (билдер) зависит от Ф0–Ф2 (что строить) + Ф3 (куда сохранять).
  • Ф5 (каталог) зависит от Ф3 (БД) + Ф0 (адаптер LabRegistry).
  • Ф6 (раздача) зависит от Ф3+Ф5.
  • Ф7 (доска) зависит от Ф0 (рантайм) + Ф5 (источник sim) + существующего simOpen/simState.

Implementation Notes

  • Каждая фаза должна оставлять /lab рабочим (Incremental).
  • Тестировать рантайм Ф0–Ф2 рукописными спеками-фикстурами (без билдера).
  • Reuse > переписывание: сначала смотреть _fx_motion, _graph_panel, graph.js.

RESUME STATE

  • Последний коммит фичи: — (Ф0 + Ф1 + Ф2 + Ф3 реализованы, ещё не закоммичены — ждут оркестратора)
  • Текущая фаза: Phase 3 — Persistence + API ( Implemented, pending commit) → дальше Phase 4 — Builder UI
  • Режим: Automated / Orchestrator / Incremental
  • Номер миграции Ф3: 071 (071_custom_sims.sql); следующая свободная — 072.
  • Новые публичные API для следующих фаз: window.SimExpr, window.SimEngine.mount, window.SimPhysics (step/integrate/resolveCollisions), window.registerSpecSim / window.SimAdapter. Формат спеки v1 + типы plot/readout/drag/vector + блок physics/body/springs/walls — в шапке _sim_engine.js и в handoff phase-0/1/2.
  • API персистентности (Ф3): /api/custom-sims (GET /, GET/PUT/DELETE /:id, POST /) + клиент LS.customSimsList/Get/Create/Update/Delete. Контракт спеки на вход/санитизация — в handoff phase-3.
  • Файлы Ф2 (несведённые с параллельной сессией): frontend/js/labs/_sim_engine.js, frontend/js/labs/_sim_demo.js.
  • Файлы Ф3: backend/src/db/migrations/071_custom_sims.sql, backend/src/controllers/customSimController.js, backend/src/routes/customSims.js, backend/tests/custom-sims.test.js (new); backend/src/server.js, js/api.js (точечные добавления). lab.html/lab-glue.js НЕ тронуты.
  • Для Ф4 (билдер): слать/получать спеку через LS.customSimCreate/Update/Get; сервер вернёт спеку санитизированной (escaped-текст). Лимиты/коды 400 — см. handoff phase-3.