- MatchTracker (codex #1 Low): event-log поля переименованы под
telemetry-spec.md v3.10 — ballOwner→ballOwnerBeforeHit,
matchTimeSec→matchTimeSeconds (для bumper_hit и goal events).
Future-proof для batched emit через .getEvents(). Тест assertion
обновлён под новые поля.
- Touch zone labels (codex #2 Low): BOOST 14→18px, alpha все
лейблы 0.5→0.7 для mobile readability.
- Test comment (codex #3 editorial): «player получает hits через
ai_setup» → корректная формулировка про defensive trap
(атакующий игрок кормит AI как защитника).
50/50 tests, typecheck/lint/build ✅.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Реализует финальную модель scoring из data-contracts.md v3.10:
fixed center bumpers — по last-touch; setup-bumpers — defensive trap
(награждают владельца только когда мяч принадлежит сопернику).
Self-farm запрещён. Neutral ball не даёт очков нигде.
Code changes:
- types/index.ts: BumperScoreSource union ('fixed_last_touch' |
'player_setup' | 'ai_setup'); MatchResult.aiBumperPointsEarned +
aiBumperHitsCount.
- Bumper.ts: новый required field source: BumperScoreSource;
передаётся в onHit callback через bumper.source.
- scoring/MatchTracker.ts: pure helper computeCreditedTo(source, ballOwner)
→ реализует 9-cell credit matrix; recordBumperHit принимает
(rawPoints, source, ballOwner, type, ...) и возвращает {creditedTo,
pointsEarned}; новые getter'ы getAIBumperHits + getUncreditedBumperHits.
- scenes/MatchScene.ts:
* Fixed bumpers создаются с source='fixed_last_touch'
* placeSetupBumpers передаёт source per side
* handleBumperHit (новая сигнатура: bumper, points) рутит в tracker,
emit'ит spawn-particles + telemetry bumper_hit event с полным
набором полей per telemetry-spec.md v3.10
* MatchResult заполняется с AI BP/hits
* match_end telemetry также включает aiBumperPointsEarned/HitsCount
* HUD: BP теперь сравнительный «P x / AI y»
- scenes/ResultScene.ts: breakdown показывает обе стороны BP и hits.
Particle effects (DoD v3.10):
- 12 псевдо-частиц радиально от bumper'а (cubic ease-out, 320ms)
- цвет: player=magenta, ai=cyan, null=grey
- screenshake 80ms/0.0025 ТОЛЬКО на credited hit
(иначе экран дрожит на каждом нейтральном отскоке)
Tests: scoring/MatchTracker.test.ts — 21 теста:
- 9-cell credit matrix (computeCreditedTo, все source × ballOwner)
- recordBumperHit с counter assertions
- многократные hits (накопление BP per сторона)
- симметрия player/AI defensive trap scenarios
- fixed bumpers через смену владения
- events log для telemetry
- recordGoal с autogoal-flag
Итого 50/50 unit-тестов (16 calculateScores + 13 flipperGeometry +
21 MatchTracker). typecheck/lint/build все зелёные.
Несоответствия с docs остающиеся:
- DoD: «12 частиц per credited side» — done; SFX bumper hit (DoD line 247)
— TODO Phase 4 (asset pack).
- Particle effects через psecdoupartials (Arc + tween), не через
ParticleEmitter — упрощение, визуально эквивалентно.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Critical bug (codex #1 High): концы нижних флипперов скрещивались В gap'е
(left.x=401, right.x=319 — оба в 306..414, причём X-shape) и торчали НИЖЕ
линии ворот (y=1275 vs playerGoalY=1260). Это блокировало корректный
gameplay — флипперы стояли крестом в створе.
Дополнительно: AI флипперы имели НЕВЕРНУЮ ориентацию (формула +180°
вместо y-flip) — concы торчали ВЛЕВО-ВНИЗ от pivot, наружу от AI ворот.
AI просто не отбивал мячи геометрически правильно.
Fixes:
- src/game/flipperGeometry.ts: pure utility — computeFlipperAngleDeg
(sign × ownerSign × baseAngle = правильный y-flip между player/AI)
+ computeFlipperEnd (вычисление end position).
- src/game/Flipper.ts: использует утилиту; удалено +180 для AI.
- src/scenes/MatchScene.ts: pivot offset 100px по x (за gap edge) и
80px по y (над goal line). Применено для всех 4 флипперов
(player+AI × left+right).
HUD (codex #2 Medium): BP перенесён с y=GAME_HEIGHT-60 (= в зоне
флипперов/BOOST overlay) на y=145 (под таймером). Управление и HUD
теперь визуально разнесены.
Tests: добавлен src/game/flipperGeometry.test.ts — 13 проверок
(угол-симметрия player/AI, инвариант «end НЕ в gap И НЕ за линией»,
swing direction, regression-test для бывшего X-скрещивания). Итого
29/29 тестов зелёные.
codex #3 (npm audit) — без изменений, документирован в KNOWN_ISSUES.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- KNOWN_ISSUES.md: dev-only vite/esbuild moderate CVEs с impact analysis
(production бандл не затронут), mitigation, плановый vite 5→8 + vitest 1→3
апгрейд перед Phase 5; AI distinguishability caveat с ссылкой на playtest.
- playtest-protocol.md: 20-матчевая blind-attribution методика для закрытия
Phase 2 DoD «тестер различает 4 личности»; рубрика per-match, expected
tells (для оценщика, не тестера), randomization protocol, failure-mode
follow-ups, report template, codex-automation roadmap.
- README.md: новый раздел «Operational docs» с cross-ref на оба файла.
Не код — операционная документация для closure Phase 2 и трекинга backlog.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Mobile match controls (codex #1 High): 3 touch zones внизу MatchScene
(L / BOOST / R). Visible semi-transparent overlay'и; работает и на mouse,
и на touch. Keyboard input теперь optional (this.keys?), не падает на
headless mobile.
- Turbo first-hit bug (codex #2): guard на lastHitTime > 0 — иначе первый
удар по turbo в первую секунду давал 0 очков (now < 0 + 1000).
- AI behavior diff (codex #3): добавлен PersonalityModifier (triggerDistBonus,
reliabilityMul, reactionMul). Defensive: +reliability, -reaction.
Aggressive: +trigger distance, -reliability. Ghost: 15% «эхо-double-swing»
(обе стороны одновременно). Trickster уже имел jitter modifier.
Поведенческие отличия теперь должны быть различимы за 5 матчей.
- Favicon 404 (codex #4 Low): inline SVG favicon в index.html, no more
console noise.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Curve bumper: spin-modifier ±35° на отскоке (детерминированный знак на bumper)
+ визуальный вихрь-индикатор. Теперь curve механически отличается от standard.
- Easy/Medium AI delta: распознавательная дистанция (Easy 150px / Medium 280px),
reliability (Easy 0.75 / Medium 0.95). difficulty прокидывается из каждой
personality в shouldActivateFlipperBase. Hard остаётся placeholder для Phase 3.
- Share button на ResultScene: snapshot canvas → PNG blob → platform.shareImage().
Mock логирует, YG использует SDK share. Telemetry: share_clicked / share_failed.
- Setup UX переписан: палитра типов сверху (4 типа + trash) + tap-палитра-tap-слот
(unified mobile/desktop) + drag-from-palette ghost (desktop). Long-tap слота без
выбранного типа удаляет содержимое (быстрый shortcut). Заменил cycle-on-click
на normalуй setup, как в DoD.
Closes codex finding #2 (Phase 2 DoD gap).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Fix#1 (Critical): rename private `data` → `sceneData` in Match/Setup/Result
scenes (collided с Phaser.Scene.data DataManager, ломало TS build).
Убран unused `game` var в main.ts. typecheck + build теперь зелёные.
- Fix#3: matchId генерируется один раз в MatchScene.create() и
переиспользуется в match_start, match_end, MatchResult — telemetry funnel
теперь связывается.
- Fix#4: AI setup сохраняется в `aiSetupConfig` и кладётся в MatchResult.aiSetup
(раньше всегда пустой). Также убрана двойная инстанциация AI
(createAIPlayer() вызывался дважды).
- Fix#5: last-touch меняется на ЛЮБОМ касании флиппера (активном или
пассивном). Раньше owner не обновлялся при пассивном отскоке → ломалась
autogoal-логика.
- Lint: убран unused `oldOwner` параметр в spawnBall callback.
- Tests: добавлены 16 unit-тестов для calculateScores
(`src/scoring/calculateScores.test.ts`) — покрытие baseline, mode/difficulty
multipliers, penalty стэкинг, double-points (local-only), Anti-A2W
invariant (base ≤ local при penalty без double-points).
- package-lock.json закоммичен для reproducible build.
Codex findings #2 (Phase 2 DoD: drag-and-drop setup, curve bumper physics,
Share button, Easy/Medium delta) — отдельным заходом.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>