Merge dev → main (automated) - #604
Merged
Merged
Conversation
added 28 commits
July 21, 2026 00:17
…H000J37N) Implements the 'Sell cards in Main Street' feature allowing players to sell placed business and community-space cards during the MarketPhase. Key changes: - Add soldSlots: boolean[] to MainStreetState and MainStreetSerializedState - Add sellBusiness() to MainStreetMarket with Math.ceil(50% refund) formula - Add canSellBusiness() legality check (MarketPhase only, not in placement mode) - Add executeSell() to MainStreetEngine for action dispatch - Add sellBusinessCommand() to MainStreetCommands with snapshot-based undo - Add totalUpgradeCost tracking on BusinessCard for accurate refunds - Update computeIncome, computeSynergyBonus, computeReputationPerTurn, computeSynergyPairs, applyIncome to exclude sold slots - Update computeHandCardSynergyBonus to skip sold slots - Add sell click handler on placed cards in MainStreetRenderer - Add dimmed 'SOLD' overlay for sold cards in renderer - Add showSellConfirmation overlay dialog with Sell/Cancel buttons - Add onSellCard() handler in MainStreetTurnController - Add migration support for legacy saves without soldSlots - Add comprehensive test suite (25 tests covering all ACs) Acceptance criteria: AC1: Click placed card opens sell dialog with card info + Sell/Cancel buttons AC2: Math.ceil((purchasePrice + upgradeCosts) / 2) refund AC3: Card stays on grid with dimmed visual, no income/synergy/reputation AC4: Undoable via existing undo system AC5: Upgrades lost (included in refund calculation) AC6: Non-functional for all game calculations AC7: Only during MarketPhase, not in card-placement mode AC8: Full test suite passes AC9: Build succeeds with no errors Closes CG-0MQOA5U4H000J37N
…logs (CG-0MRP1AY0L008J5VL) Adds a new 'UI Best Practices: Creating Modal Dialogs' section to AGENTS.md documenting the correct pattern for creating overlay dialogs, using the sell confirmation dialog as the reference implementation. Key lessons documented: - Use createOverlayBackground / createOverlayButton / dismissOverlay from @ui - CRITICAL: Parent ALL overlay text/buttons into hudContainer - Depth ordering (backdrop 199 < box 200 < text/buttons 201) - Cleanup pattern with dismissOverlay - Reference to showSellConfirmation for a complete example Closes CG-0MRP1AY0L008J5VL
…G-0MQN31CD400709UC) The game-over overlay in Feudalism unconditionally displayed "Tiebreak: fewest cards wins" even when the winner had a clear influence lead. This was confusing to players. Changes: - FeudalismOverlays.ts: conditionally append tiebreaker text only when humanInfluence === aiInfluence - FeudalismGameOverOverlay.test.ts: unit tests verifying tiebreaker display logic and getWinnerIndex consistency ACs verified: 1. Tiebreaker text hidden on non-tie wins ✓ 2. Tiebreaker text shown on tie wins ✓ 3. Score summary unchanged ✓ 4. No regression on winner determination ✓ 5. All 240 test files pass (4293 tests) ✓ 6. Build succeeds ✓
…TVIH0052HW0) Changes: - Add OVERLAY_DEPTH (2000) and OVERLAY_BG_ALPHA (0.01) constants to FeudalismConstants.ts (following Beleaguered Castle pattern) - Pass background depth 2000 to createGameOverOverlay in FeudalismOverlays.ts, matching the standard 'game-over' overlay type (depth 2000) instead of the default depth 10 - Update z-order test docs to reflect new overlay depth range All ACs met: 1. Overlay uses depth 2000 (was defaulting to depth 10) 2. Standard button positions (already correct via createGameOverOverlay) 3. Content display preserved (no text/layout changes) 4. Transcript saving preserved (no behavioral change) 5. Full test suite passes (4293 tests, 0 failures) 6. Documentation updated (z-order test, constants with comments)
…ushi Go When reduced motion is enabled, animations are skipped which can make the AI's turn feel instantaneous and lacking perceptible thinking time. Changes per game: - Golf (GolfAiController): adds SWAP_ANIM_DURATION (450ms) extra delay - Feudalism (FeudalismTurnController): adds MOVE_DURATION (700ms) extra delay - Lost Cities (LostCitiesTurnController): adds ANIM_DURATION (300ms) extra delay - Sushi Go (SushiGoScene): adds ANIM_DURATION (300ms) extra delay Each game's scene propagates the settingsPanel reducedMotion preference to its AI controller/turn controller, which uses it to conditionally increase the AI delay to match the skipped animation duration. Closes CG-0MQPSVDTD002JCX5
…40072RL9) - Add new ms-icon-stats.svg 16x16 bar chart icon following the existing icon visual pattern (colored background + white semi-transparent bars) - Add 'stats' to the preloaded icons list in MainStreetLifecycleManager - Update StatsButton to display the SVG icon via Phaser Image when the ms-icon-stats texture is available, with graceful fallback to the Greek Sigma Σ text label when the texture is not loaded - Hover effects updated: tint the icon white (or text white) on hover, restore gold tint (or text color) on pointer out - Write tests: SVG asset validation, preload integration, StatsButton class structure with icon/fallback references
…ing (CG-0MQR54BSD007FQTA) Add fetch-based pre-check in loadMainStreetTfModule() to verify the synth module exists before attempting dynamic import(), avoiding the Chromium 'Failed to load module script' console error when the file is missing. Changes: - Add checkSynthModuleExists() helper that fetches the module URL and detects HTML fallback responses (Vite dev server behavior for missing paths) - Log clear [MainStreet] console.warn with instructions to run 'npm run tf:generate' when module is not found - Update TfGeneratedModule cached type to accept null for caching the 'not found' state - Add two new tests: (1) HTML response returns null with warning, (2) network error falls through gracefully - Update docs/DEVELOPER.md with ToneForge quick-start note and graceful degradation documentation Closes CG-0MQR54BSD007FQTA
… to card type documentation tables
The substantive fix (reducing HUD strip width from 66% to 50%) was already applied by CG-0MQU3AREL0099R1I (commit 0ba4c3b). This change only fixes the stale comment that still said "2/3 width".
Implements the rule that synergy bonuses (coin and reputation) only apply between businesses of **different base types** (template IDs). Same-type adjacent businesses: - Receive 0 synergy from each other (both coin and reputation) - Have their base income reduced to 60% (applied once, not stacked) Changes: - Added getBaseTypeId() helper in MainStreetCards.ts — strips the serial suffix (-N) from card IDs to identify the base template - Added hasAdjacentSameType() helper in MainStreetAdjacency.ts — checks if a business has at least one adjacent same-type neighbor - Modified computeSynergyBonus() — skips same-type neighbors - Modified computeSynergyRepBonus() — skips same-type neighbors - Modified computeBusinessIncome() — applies 0.6 multiplier when adjacent to same-type business - Modified computeIncome() — applies the same 0.6 multiplier in breakdown - Modified computeSynergyPairs() — excludes same-type pairs from visual synergy lines - Updated clinic-health-synergy.test.ts — adjusted expected rep values for same-type Clinic adjacency (synergy rep nullified) - Added same-type-synergy.test.ts — comprehensive tests covering all ACs - Updated docs/main-street/core-rules-and-mechanics.md
Refactors drawMarketRow to accept an optional alignmentStartX parameter. When rendering the investments row, passes the development row's startX so investment card slots (3 total) sit at grid columns 0, 1, 2 — directly below the first three development cards. Changes: - MainStreetRenderer.ts: compute devStartX in refreshMarket(), pass to both drawMarketRow calls; drawMarketRow uses alignmentStartX when provided instead of independent centering. - tests/main-street/market-row-alignment.test.ts: new test file with 7 tests verifying the alignment math.
- Deleted orphaned scripts/generate-mind-sfx.mjs - Deleted stale screenshots at tests/ui/__screenshots__/TheMindMigration.browser.test.ts/ - Updated docs/DEVELOPER.md: removed all The Mind references, genericized migration pattern section - Updated README.md: removed The Mind from directory listing - Updated docs/SFX_CONVENTION.md: removed the-mind from audio structure - Updated docs/the-build/engine-capabilities-audit.md: removed The Mind section and genericized timer reference - Updated public/assets/CREDITS.md: removed The Mind card and audio attribution sections - Updated .pi-compact.md: removed TheMind from code-smell refactoring line
…i Go, Main Street, Lost Cities Adds a [Menu] button to the game-over overlays of three games that had custom overlays excluded from the shared createGameOverOverlay migration: - Sushi Go: Added Menu button via createActionButton next to Play Again - Main Street: Added Menu button via createOverlayButton next to Play Again - Lost Cities: Added Menu button via createActionButton next to New Match All Menu buttons dismiss the overlay and navigate to GameSelectorScene. Added browser tests verifying Menu button existence and interactivity for all three games. Created child work items for migrating each custom overlay to the shared createGameOverOverlay component. ACs: 1. ✅ Each game displays a [Menu] button navigating to GameSelectorScene 2. ✅ Menu buttons styled consistently with existing overlay buttons 3. ✅ [Play Again] / [New Match] still works identically 4. ✅ Games using shared overlay (Golf, Beleaguered Castle, etc.) not modified 5. ✅ Child work items created for migration 6. ✅ Tests added for Menu button verification 7. ✅ Full test suite passes
Replaces monolithic per-turn income/reputation recalculation with per-card cached effective values (currentIncome/currentReputationPerTurn) that are incrementally updated when cards are placed, sold, or upgraded. Changes: - Added currentIncome and currentReputationPerTurn fields to BusinessCard and CommunitySpaceCard interfaces (optional, undefined until placed) - Added syncCardCurrentIncome, syncCardCurrentRepPerTurn, recalculateCard, updateNeighborsOnPlacement, and updateNeighborsOnSale functions in MainStreetAdjacency.ts - Modified applyIncome() to read cached values instead of recomputing from scratch each turn, with fallback to computation when cached values are undefined (legacy saves, direct grid manipulation) - Modified purchaseBusiness, sellBusiness, and purchaseUpgrade in MainStreetMarket.ts to call incremental update functions - Modified placeFromHand and sellFromTableau in MainStreetEngine.ts to call incremental update functions - Added legacy migration (no explicit migration needed - undefined fields trigger fallback computation) - Added incremental-income.test.ts with 28 tests covering field init, placement/sale recalculation, same-type penalty, save/load round-trip, and integration scenarios - Updated community-space-types.test.ts fixture to include new fields
Root cause: Three overlapping sound paths fired the same SFX keys per
game action — event system (connectToEvents), flipCard sfx config, and
explicit soundManager.play calls in GolfAnimator tweens.
Fix:
- GolfScene: Remove card-movement events (card-drawn, card-flipped,
card-swapped, card-discarded) from EventSoundMapping. The animation
layer in GolfAnimator already plays the corresponding SFX.
- GolfAnimator: Remove all periodic/redundant sound calls
- animateSwap: Remove move sfx from flipCard config and
lastMove/onUpdate tracking from drawn-card tween
- animateDiscardAndFlip: Remove start/move sfx from flipCard
(discard was already played in animateDrawnCardToDiscard)
- showDrawnCard (stock): Remove move sfx from flipCard config
- showDrawnCard (discard): Remove onUpdate periodic CARD_DRAW calls
- animateDrawnCardToDiscard: Remove onUpdate periodic and onComplete
duplicate CARD_DISCARD calls
Each action now plays each unique SFX key at most once.
- Draw: CARD_DRAW (start) + CARD_FLIP (midpoint, stock only)
- Swap: CARD_SWAP (start) + CARD_FLIP (midpoint, grid card reveal)
- Discard-and-flip: CARD_DISCARD (discard phase) + CARD_FLIP (flip phase)
- Reduced motion preserved (no sounds when active)
Tests added: GolfSoundDuplication.test.ts (13 tests) verifying the
event mapping excludes card events and animator has no periodic
or duplicate sound calls.
… in PRD Adds explicit documentation in prd-milestone-2.md that Clinic was reclassified from Service to Health synergy, along with updated references to 6 synergy types (was 5). Closes CG-0MQRB9RMF003PRNO
…GSS0080N9P) Replaces the fully duplicated Worklog rules section (~200 lines copied from the global AGENTS.md at ~/.pi/agent/AGENTS.md) with a concise reference section that points to the global file and summarizes only TCE-specific conventions (prefix, priorities, stages). Net reduction: 401 -> 224 lines (44% smaller). - All project-specific sections preserved: Components, Directory Structure, Development Workflow, Technology Stack, Licensing, Doc-Update Policy, UI Best Practices: Creating Modal Dialogs - Duplicated work-item tracking rules replaced with cross-reference
…ename Rep to Reputation Per audit rejection (rgardler, 2026-07-22): - Left-align Coins text in the HUD strip - Right-align Score text in the HUD strip - Center Reputation (renamed from 'Rep') text - Update animation popup positions (coinX, repX) to match new layout - Update browser test to check for 'Reputation:' prefix CG-0MQS25QSO0031OHU
The HUD tooltip income display computed by buildCoinsTooltip() and getIncomeResult() was not passing state.soldSlots to computeIncome(), defaulting to an empty soldSlots array. This caused sold cards to still appear as generating income in the HUD tooltip display, even though applyIncome() correctly excluded them from actual game logic. Fix: pass state.soldSlots to both computeIncome() calls so that sold cards are excluded from the displayed total. Test: added 'excludes sold cards from income display' test verifying both computeIncome() and buildCoinsTooltip() return 0 after selling. Work item: CG-0MQOA5U4H000J37N
…Income() The HUD tooltips (buildCoinsTooltip, getIncomeResult) were calling computeIncome() which does a full grid recalculation from scratch. After CG-0MRV84ZT60069PW6, each card has a cached currentIncome field that is incrementally updated on placement/sale/upgrade. The tooltips now sum these cached values with a fallback to computeBusinessIncome for cards without cached values (legacy saves, test setups). This also correctly excludes sold card income: sold slots are skipped during iteration, so the HUD display matches the actual income phase. Work item: CG-0MQOA5U4H000J37N (audit gap)
Previously, applyIncome() and the HUD tooltips fell back to computeBusinessIncome() when currentIncome was undefined on a card (legacy saves). Per design decision: legacy saves are not supported. Changes: - applyIncome(): use card.currentIncome ?? 0 instead of falling back to computeBusinessIncome (same for reputation per turn) - buildCoinsTooltip, getIncomeResult: use card.currentIncome ?? 0 - Removed unused computeBusinessIncome/bonusPerNeighbor imports - Updated legacy migration test to expect zero income for cards without currentIncome - Updated all tests that place cards on the grid to call recalculateCard before applyIncome Work item: CG-0MQOA5U4H000J37N
… to percentage multipliers Core changes: - MainStreetAdjacency.ts: Changed default from to . Rewrote to use percentage-based formula: synergy = effectiveBase * rate * bonusPerNeighbor * N where rate comes from the source card's synergyCoinBonus, not the neighbor's. Synergy-neutral cards (synergyCoinBonus=0 AND synergyRepBonus=0) are skipped in neighbor counting. - MainStreetDifficulty.ts: Repurposed synergyBonusPerNeighbor as a multiplier: Easy=1.5, Medium=1.0, Hard=0.75. Updated JSDoc. - MainStreetCards.ts: Updated SYNERGY_BONUS_PER_NEIGHBOR deprecated comment. - card-data.csv: Changed synergyCoinBonus from 1 to 0.5 for biz-gallery, biz-spa, biz-clinic, biz-private-clinic, cs-library. Pawn Shop stays at 0. - csv-checksum.json: Updated to reflect CSV changes. - docs/main-street/core-rules-and-mechanics.md: Updated synergy description and income phase formula. - monte-carlo-baseline.json: Regenerated with new balance. Test updates: - adjacency.test.ts: Updated expected values for percentage-based formula. - same-type-synergy.test.ts: Updated makeBiz defaults to 0.5 and expected values. - expanded-card-pool.test.ts: Updated Clinic and Cinema expected values (4→4.5). - clinic-health-synergy.test.ts: Updated Private Clinic/Pharmacy synergy values. - MainStreetHandSynergy.test.ts: Updated makeBiz default from 1 to 0.5.
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.