Skip to content

Merge dev → main (automated) - #793

Merged
SorraTheOrc merged 37 commits into
mainfrom
release/dev-to-main-20260805232525
Aug 5, 2026
Merged

Merge dev → main (automated)#793
SorraTheOrc merged 37 commits into
mainfrom
release/dev-to-main-20260805232525

Conversation

@SorraTheOrc

Copy link
Copy Markdown
Member

Automated release created by ship skill.\n\nIncludes CHANGELOG.md with work-item summaries from this release.

Sorra the Orc 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).
…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
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
Sorra the Orc added 7 commits August 5, 2026 15:19
… 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).
@SorraTheOrc
SorraTheOrc merged commit 1d9521e into main Aug 5, 2026
@SorraTheOrc
SorraTheOrc deleted the release/dev-to-main-20260805232525 branch August 5, 2026 23:26
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant