Merge dev → main (automated) - #793
Merged
Merged
Conversation
added 30 commits
July 31, 2026 23:40
CG-0MQIU6EYN005CBI5 Creates example-games/gym/scenes/GymConstants.ts centralising ~170 named constants (viewport, typography, layout offsets, event log config, animation durations, colours, spike/tooltip/overlay/hand/scene-specific values) and updates all 14 in-scope Gym scene files to import from it instead of hardcoding numeric/string literals in function bodies. - GymSceneBase.ts: nav buttons, divider, help panel width, transitions - GymAudioFeedbackScene.ts: volume, pop text, celebration particles - GymDeckRngScene.ts: seed/status/design-row offsets - GymGraphicsLightingSpikeScene.ts: sprite dims, light params, log config - GymGraphicsShaderSpikeScene.ts: sprite dims, star/triangle shapes, status - GymHandPileScene.ts: drop delays, highlight/zone colours, animation timings - GymHudComponentsScene.ts: panel status, depth info, event log - GymOverlayUiScene.ts: mask, scrollbar, intensity, overlay text - GymRouterScene.ts: title/subtitle, grid breakpoints, card text offsets - GymSaveLoadScene.ts: state/backend text, thumbnail position - GymSllScene.ts: control row, demo objects, overlay legend - GymTooltipScene.ts: mode/status labels, card tooltip styling, log - GymTranscriptScene.ts: blackjack rules, dealer delays, pop text - GymUndoRedoScene.ts: counter/history fonts, status offsets Adds tests/gym/GymConstants.test.ts verifying constant values match the original literals so visual behaviour cannot drift silently. Visual behaviour unchanged. npm test (4572 unit + 414 browser + 12 e2e) and npm run build both pass.
…(CG-0MROXIJZ3004VGUI) createCardBackTokenRenderer appended cardType to the back texture key (e.g. gym_token_card_back-treasure) but scenes that generate only a single back texture never registered those variants, so card-back tokens rendered as Phaser missing-texture sprites in GymTokenPileView. - Add texture-existence check in createCardBackTokenRenderer; when a cardType-variant texture is missing, fall back to the base backTexture and log a console.warn for developer visibility. - Add getCardBackPileContainer() test accessor to GymTokenPileViewScene. - Unit tests: variant-missing fallback, variant-present path, base path. - Browser test: card-back pile renders visible sprites across load/add/remove/reset (AC 1-4). - Document fallback behavior in docs/ui/ADAPTER-GUIDE.md.
… cost Library (cs-library) is repurposed from a weak income card into a culture investment: no income, 0.25 coins/turn ongoing cost, +0.1 reputation/turn, synergy-neutral, cost 7. The Community Hub upgrade (upg-community-hub) now grants +0.1 reputation/turn instead of income/range bonuses, cost 4. Engine: CommunitySpaceCard gains ongoingCost (interface, CSV parsing, makeCommunitySpace default); the income phase deducts community-space ongoing costs alongside staff costs (clamped at 0 coins, logged); legacy saves get ongoingCost=0 migration. UI: community-space tooltip shows an Ongoing cost line; runtime SVG generator shows -X/turn on the card face; static SVGs + checksum regenerated for the new data. Docs: card-catalog, core-rules-and-mechanics (community-space anatomy + ongoing-cost rule), balancing-methodology (Special Case note). Tests: updated community-space types/tooltip tests, new community-space-ongoing-cost.test.ts (deduction, clamp, upgrade). Closes CG-0MRXYGM9B006I3PE (children F1-F5: CG-0MS9HJTE4009LQE1 CG-0MS9HJ6AL0087DPB CG-0MS9HJ8SU008MJQQ CG-0MS9HJBJH0059GRK CG-0MS9HJEL9000DX67)
Buy transfers in Main Street animated to a left-edge slot estimate (handX + handCardW/2 + N*spacing) instead of the actual HandView resting position (centred on handCenterX), causing the flying card to snap sideways when the hand re-rendered. - Add HandView.getInsertionPosition(insertIndex) as the single source of truth for predicted hand positions, sharing the layout code path with computeCardPositions/animateAddCard; animateAddCard now delegates to it. - MainStreetScene: add getBusinessHandInsertionPosition / getEventHandInsertionPosition delegations to the renderer's HandViews. - MainStreetTurnController: onBusinessCardClick uses the business-hand insertion position (append index = current hand length); onEventCardClick uses the event-hand insertion position (index 0). - MainStreetAnimator.getHandCardCenter kept for backward compatibility (buy transfers no longer use it). - Tests: HandView getInsertionPosition unit tests (append/front/middle, arc, vertical, clamping), animateAddCard destination equality, turn controller destination equality (business + event, incl. hand-full guard), and browser assertions at 1280x720 and 900x1100 that the transfer destination equals the rendered hand position. - Docs: example-games/main-street/README.md hand-layout-prediction section.
…tion assertions Cast vi.spyOn call args to any when reading the animateTransferFromMarket destination, since vitest 3 types mock.calls entries as unknown for object-method spies on Record<string, any> scenes.
…ergy bonuses Audit finding (CG-0MRXYGM9B006I3PE AC3): with synergyCoinBonus/synergyRepBonus left empty, the engine's effectiveSynergyCoinBonus defaults to 0.5, so the Library is not treated as synergy-neutral — it can receive reputation synergy from Culture neighbors with a rep bonus and counts toward neighbors' synergy count N. Use the Pawn Shop pattern: explicit 0/0 opts the card out of the synergy system entirely (MainStreetAdjacency computeSynergyBonus / computeSynergyRepBonus early-return for zero rates). - card-data.csv: cs-library synergyCoinBonus=0, synergyRepBonus=0 - Tests: synergy-neutral assertions updated to expect 0; new behavioral tests prove the Library receives no coin/rep synergy from a matching Culture neighbor and is not counted toward a neighbor's synergy. - Docs: card-catalog + balancing-methodology note the explicit 0/0 pattern.
…F0008QIZY) Create project-level agent skill at .pi/skills/tce-game-dev/SKILL.md capturing all TCE game-development conventions: architecture patterns, SLL layout, scene lifecycle, HUD/UI components, audio, accessibility, data persistence, AI strategies, testing, CI/CD, card-system patterns, shared engine modules, modal dialog conventions, common pitfalls, and a game-review checklist with explicit completeness-review steps for all Gym scenes, example games, GymRegistry, package.json, and CI/CD. Update .gitignore to track project-level agent skills under .pi/skills/ while keeping local pi settings (.pi/settings.json) ignored. All 5 child work items (4 research reviews + write task) completed; build and full test suite (unit + browser + tutorial e2e) pass.
…9J4PNV00349RI) Boots BlackjackScene via createBlackjackGame (Phaser CANVAS) and verifies: - Hands render through HandView instances exposed on the scene with sprite counts matching the model hands (deal/hit/stand flow) - Dealer hole card renders card_back before stand and the face-up card texture after stand - Reduced-motion mode applies the hole-card reveal instantly These tests are the test-first gate for the Blackjack HandView migration (CG-0MS9J4SDD002AR3E) and are expected to be red until that migration lands.
…002AR3E) Replace bespoke sprite-array hand rendering with core-engine components: - playerHandView/dealerHandView HandView instances anchored via SLL (centerX keeps rows centred as hands grow; 125px cards / 10px gap) - renderCards() delegates to setCards(); removes renderPlayerCards(), renderDealerCards(), clearCardDisplays() and the sprite arrays - deal/hit slide-from-centre animations preserved, positions snapshotted from HandView getSprites()/getCardCenters() - hole-card reveal now uses the shared flipCard() helper with reduced-motion support - settings-panel reduced-motion preference wired into both hand views Makes the HandView browser tests (CG-0MS9J4PNV00349RI) pass. Full suite (unit + browser + tutorial) and production build verified.
…9J4VBL0037SPB) - AGENTS.md best practice #2: upgrade HandView/PileView from a suggestive 'When to use' note to a hard requirement — example games must render hands/piles through core engine components; bespoke hand rendering is not an accepted pattern. - Documented exception carve-outs: Golf 3x3 tableau grid (GolfRenderer.ts note) and Feudalism token/crop counters (CropIconRenderer), with guidance that exceptions must be justified in code comments/help text. - Blackjack (BlackjackScene.ts) cited as the canonical reference for standard hand rendering. - docs/DEVELOPER.md: new 'Hand & Pile Rendering' subsection after 'Adding an Example Game', cross-linking ui/ADAPTER-GUIDE.md and gym/GYM_INDEX.md; TOC updated.
- Add GAME_WIN/GAME_LOST ('sfx-game-win'/'sfx-game-lost') keys to Golf SFX_KEYS
- Load game-win.wav / game-lost.wav in GolfScene.preload() (golf/ + default/)
- Remove 'game-ended' -> ROUND_END event mapping; play win/loss sound
directly in showEndScreen() based on winner (human win -> game-win,
AI win -> game-lost), keeping sfx-score-reveal
- Copy CC0 win/loss WAVs from Lost Cities synth set into default/ (mirrored
in golf/ since the Phaser audio loader does not fall back on 404)
- Add unit tests (SFX key convention + asset existence) and browser tests
(win/loss sound playback on end screen)
- Update docs/SFX_CONVENTION.md and public/assets/CREDITS.md
- New BeleagueredCastleAi.ts: hybrid solver (depth-limited winning-line search with top-K pruning + heuristic fallback) using shared @ai abstractions (AiStrategyBase, AiPlayer, pickBest); foundation moves evaluated first with a root bonus and early exit on a winning line. - Scene: Hint button in HUD (left of Undo) wired to the AI player; requestHint() highlights source (gold) and destination (green) and shows a description via the shared HintBar; hint hides on board refresh; blocked during deal/end/auto-complete. - Renderer: showHint/clearHint/hasActiveHint with HINT_DEPTH rects. - Help content: new 'Hints' section. DEVELOPER.md: directory listing + feature table updated. - Tests: BeleagueredCastleAi.test.ts (13 unit) + BeleagueredCastleHint .browser.test.ts (4 browser).
…tions Add canPlaceFromHand, canSellFromHand, and canSellFromTableau to MainStreetEngine.ts. Each validates its relevant bounds/occupancy conditions and returns a LegalityResult without mutating state. Adds dedicated tests for the sell wrappers and a non-mutation test, plus the two previously-skipped legality-gated placement tests now run and pass (insufficient/sufficient coins).
…meouts Replay CLI tests: spawnSync timeout was 15s but tsx + Playwright module loading routinely takes 15-30s under parallel load, producing empty stdout and false failures. Bump spawn timeout to 60s and vitest test timeouts to 90s. The v1-transcript test's 10s kill never captured the version header (module loading alone takes ~15s); raise to 60s. generate-thumbnail test: the script boots a Phaser scene via Playwright headless (30-70s on loaded machines); the 15s default vitest timeout was too short. Add a 120s test timeout.
Adds maxWorkers: 4 to the unit test project in vite.config.ts to bound concurrent tinypool workers (fan-out bounding, SA-0MSAEKOQE009TEB4). Previously the unit project had no worker cap, so each vitest run spawned 15+ node worker processes; concurrent runs compounded load on the workstation. Mirrors the ContextHub cap (maxWorkers: 4, singleFork: true). Verified: 245 unit test files / 4646 tests pass with the cap in place.
…rc/ (CG-0MRDNVQPS009UUIN)
- Relocate scripts/balance-cards/{algorithm,csv,index,rationale,summary}.ts to src/balance-cards/
- Update scripts/run-balance-cards.ts to import from ../src/balance-cards; fix usage text path
- Update tests/main-street/balance-cards.test.ts import path
- Update PRD docs path references (scripts/balance-cards -> src/balance-cards)
- No functional changes: CLI output verified byte-identical
- Stabilize pre-existing flaky load-sensitive tests: replay CLI subprocess timeouts, screen-layout perf budget, generate-thumbnail, ai-strategy 200-seed, monte-carlo guardrails, lost-cities full match (timeout increases)
- Refactor scripts/generate-card-csv.ts: export buildCsvRows(tsContent) pure function + COLS; guard file I/O behind main-module check; remove the ad-hoc '// Verify specific cards' find/console.log block. - Add tests/scripts/generate-card-csv.test.ts validating the generator logic (CSV escaping, all 5 families, field counts) and the checked-in card-data.csv content (Reader's Café special chars, evt-festival, family coverage, per-row field-count integrity). - Fix 2 malformed event rows in card-data.csv (evt-flu-outbreak, evt-recession had 28 fields instead of 29). - Sync public/assets/games/main-street/svg/cards/csv-checksum.json with the corrected CSV (DJB2 996993d4 -> 71f4a00c).
…-move-csv-validation
…8n t()
Extend t(key, params) to replace {token} placeholders in resolved strings
with values from params. Missing placeholders throw instead of rendering
raw tokens; extra params are ignored. No-params calls behave exactly as
before. This is the mechanism Main Street will use to inject live card
data into tutorial text at render time.
- t(key, params?) with {token} interpolation
- throws on missing placeholder params
- unit tests for substitution, locale switching, fallback, and failure
…v skill (CG-0MRPH9YF0008QIZY)
Tutorial steps T3/T7/T8/T9 no longer hardcode card names, costs, or
income figures in the i18n bundle. Card facts are resolved at render
time from card-data.csv via requiredCardId/referencedCardId lookup.
- tutorial-en.ts: replace hardcoded card facts with {cardName}/{cost}/
{bonus} placeholders; reword T3 'cheapest card' claim (stale — the
Laundromat at 4 is not cheapest in the tutorial row)
- TutorialFlow.ts: add referencedCardId (text-only card refs), extend
resolveTutorialStepText() to interpolate live card data via
getCsvRows() + formatCurrency(); fail loudly on missing rows
- MainStreetTutorialHints.ts: use resolveTutorialStepText in DOM and
Phaser render paths
- tests: data-driven assertions + new tests (live cost match, no raw
placeholders, changed-cost updates text, per-locale interpolation)
…lization guide
- Add 'Card-data placeholders' section: {cardName}/{cost}/{bonus} tokens,
the requiredCardId/referencedCardId -> getCsvRows() -> formatCurrency()
resolution flow, and fail-loudly behavior on missing card rows
- 'Changing existing text' now instructs never to hardcode card facts
- 'Adding a New Language' documents keeping placeholder tokens in
translations
- Test-coverage table and file overview updated for the interpolation
mechanism in src/core-engine/I18n.ts
…dation error paths exit before heavy module loading
- Re-export cycleMarketCards from MainStreetEngine (was imported/used internally but not re-exported; tests accessed it via (engine as any)) - Replace racy async feature-detection IIFE with synchronous gate based on the statically imported function, so it.runIf() evaluates deterministically at collection time - 2 previously skipped tests (unpurchased-card discard capture, deterministic market difference after cycling + refill) now run and pass Verified: 17/17 tests in MainStreetMarketCycling.test.ts pass; full suite 4667 passed | 6 skipped (environmental/conditional skips, pre-existing); build succeeds.
…ten (CG-0MSBX3UA9001QDF3)
The finally block called generateContactSheet(outputDir) before writing
replay-summary.json, but contact-sheet.ts reads the summary file from disk
to find the screenshot list. The summary now written first, then the contact
sheet is generated, and the summary re-written to persist contactSheetPath.
Also fixed contact-sheet.ts to resize full-viewport screenshots (1280x720)
to the 225x175 thumbnail cells — compositing at native size broke sharp
('Image to composite must have same dimensions or smaller'), which was
masked before because the contact sheet never ran in the integrated flow.
Tests:
- New unit tests for generateContactSheet (scripts/contact-sheet.ts)
- E2E: full Main Street replay asserts contact-sheet.png exists and
summary.contactSheetPath is populated
…nner Full-suite npm test intermittently fails at teardown with [vitest-worker]: Timeout calling "onTaskUpdate" even though every test file passed, because Vitest's worker RPC layer uses birpc with a fixed 60s timeout (DEFAULT_TIMEOUT = 6e4) that is not configurable. Under CPU contention a worker can miss the window while reporting results, and vitest exits non-zero; set -euo pipefail then aborts scripts/run-ci-tests.sh after the unit step. Mitigation (scripts/vitest-run-with-retry.ts, wired into the unit step of scripts/run-ci-tests.sh): - Retry the unit run exactly once when the reporter summary shows ALL files passed AND the sole error is the worker RPC timeout signature. - Masking guard (shouldRetryOnce) proves all-passed from the summary, so a genuine test failure can never be hidden by a retry. - Unit tests in tests/scripts/vitest-run-with-retry.test.ts cover the guard and the retry-once orchestration. Docs: DEVELOPER.md Testing section documents the root cause and both mitigations (maxWorkers: 4 cap already in place; retry-once runner).
…nd card faces - Extract buildCardTooltipInfo() into MainStreetFormatting.ts: all card family tooltip branches (business, community-space, event, upgrade) now render cost via i18n formatCurrency() instead of raw numbers. - MainStreetRenderer: replace 6 inline tooltip builders with buildCardTooltipInfo() (hand event cards, market rows, held event cards). Business cards held in hand now get a family-correct tooltip instead of the hardcoded 'Event:' prefix (display-only fix). - MainStreetCardSvgGenerator: cost badge (business/community-space, event, upgrade, staff card faces) uses formatCurrency() so the symbol follows the active locale (default €, overridable per locale). - New unit tests (card-currency-formatting.test.ts) cover all tooltip branches, locale switching (AC3), and SVG card-face badges (AC2); updated card-svg-generator.test.ts cost assertions to formatted output. Gameplay values unchanged; display-only formatting.
…oinBonus doc comments (AC 7), guard checkpoint resume against scene teardown
added 7 commits
August 5, 2026 15:19
…t README (0.5, not 1) — audit AC 7
… coin' synergy prose in GDD/PRD docs with percentage formula
…nics.md CG-0MSB1FVTZ00488JY: update §4 Game State Model to match current code: - phase: DayPhase (6-phase cycle) instead of dayPhase: 'Day' | 'Night' - streetGrid supports CommunitySpaceCard - market.business -> market.development (business + community space cards) - deck -> decks (adds communitySpace deck) - challengesCompleted: string[] instead of Set<string> - 'Business row' -> 'Development row' (Key components + Randomness table)
…for vitest browser WebSocket drops The [vitest] Browser connection was closed while running tests failure is a transient browser-mode WebSocket drop: under CPU contention the browser RPC connection closes after all files completed, and vitest exits non-zero even though every test passed. The unit step already retried on the worker RPC timeout ([vitest-worker]: Timeout calling "onTaskUpdate"), but the browser step ran bare npx vitest with no retry. Changes: - scripts/vitest-run-with-retry.ts: add BROWSER_DROP_SIGNATURE and extend shouldRetryOnce to retry on either transient signature, keeping the masking guard (all files passed + no failed files/tests) unchanged. - scripts/run-ci-tests.sh: route the browser step through the retry runner. - tests/scripts/vitest-run-with-retry.test.ts: 5 new cases covering the browser-drop signature (retry on transient, never mask genuine failures). - docs/DEVELOPER.md: document both transient signatures and the browser step retry (doc-update policy). Verified: npm run build succeeds; full npm test passes (unit + browser + tutorial E2E).
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Automated release created by ship skill.\n\nIncludes CHANGELOG.md with work-item summaries from this release.