Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
147 changes: 147 additions & 0 deletions docs/core-engine/transcript-migration.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
# Transcript & SaveLoad Consolidation — Migration Guide

**Work Item:** CG-0MP12WI75001L9P4
**Date:** 2026-05-24

## Summary

All transcript-related functionality has been consolidated into a single
sub-module at `src/core-engine/transcript/`. The original top-level files
(`TranscriptRecorder.ts`, `TranscriptStore.ts`, `TranscriptTypes.ts`,
`autoSaveTranscript.ts`) remain as backward-compatible re-exports so that
existing imports continue to work without changes.

## What Changed

### New canonical location

All transcript exports are now available from `src/core-engine/transcript/`:

```
src/core-engine/transcript/
├── index.ts # Barrel file (canonical import target)
├── TranscriptRecorder.ts # TranscriptRecorderBase, BaseTranscript
├── TranscriptStore.ts # TranscriptStore, StoredTranscript, TranscriptStoreOptions
├── TranscriptTypes.ts # CardSnapshot, snapshotCard()
└── autoSaveTranscript.ts # autoSaveTranscript()
```

### Backward compatibility

The legacy top-level files now re-export from the consolidated location:

| Legacy path | Re-exports from |
|---|---|
| `src/core-engine/TranscriptRecorder.ts` | `./transcript/TranscriptRecorder.ts` |
| `src/core-engine/TranscriptStore.ts` | `./transcript/TranscriptStore.ts` |
| `src/core-engine/TranscriptTypes.ts` | `./transcript/TranscriptTypes.ts` |
| `src/core-engine/autoSaveTranscript.ts` | `./transcript/autoSaveTranscript.ts` |

The core-engine barrel (`src/core-engine/index.ts`) also exports everything
from the consolidated transcript barrel, so `import { ... } from '@core-engine'`
continues to work.

## Migration

### No action required (recommended for now)

Existing imports will continue to work without any changes. The re-export
shims ensure full backward compatibility.

### Optional: update to the consolidated import (new code)

When adding new code or refactoring, prefer the consolidated import:

**Before (old per-file imports):**

```ts
import { TranscriptRecorderBase, type BaseTranscript } from '../../src/core-engine/TranscriptRecorder';
import { TranscriptStore, type StoredTranscript } from '../../src/core-engine/TranscriptStore';
import { autoSaveTranscript } from '../../src/core-engine/autoSaveTranscript';
import { CardSnapshot, snapshotCard } from '../../src/core-engine/TranscriptTypes';
```

**After (consolidated import):**

```ts
import {
TranscriptRecorderBase,
BaseTranscript,
TranscriptStore,
StoredTranscript,
TranscriptStoreOptions,
autoSaveTranscript,
CardSnapshot,
snapshotCard,
} from '@core-engine/transcript';
```

Or via relative path:

```ts
import {
TranscriptRecorderBase,
TranscriptStore,
autoSaveTranscript,
snapshotCard,
} from '../../../src/core-engine/transcript';
```

### Via the core-engine barrel

The consolidated exports are also available through the main barrel:

```ts
import {
TranscriptRecorderBase,
TranscriptStore,
autoSaveTranscript,
snapshotCard,
BaseTranscript,
CardSnapshot,
StoredTranscript,
TranscriptStoreOptions,
} from '@core-engine';
```

## Main Street Integration

Main Street now uses `autoSaveTranscript` from the consolidated module.
When a game ends, the transcript is automatically finalized and persisted
to browser storage (IndexedDB with localStorage fallback).

This was added in `MainStreetTurnController.ts`:

```ts
import { finalizeMainStreetTranscript } from '../MainStreetTranscript';
import { TranscriptStore, autoSaveTranscript } from '../../../src/core-engine/transcript';

// In the game-end branch:
const transcript = finalizeMainStreetTranscript({
gameResult: result.gameResult,
finalScore: result.finalScore,
});
if (transcript) {
const transcriptStore = new TranscriptStore();
autoSaveTranscript(transcriptStore, 'main-street', transcript, '[MainStreet]');
}
```

Two new helper functions were added to `MainStreetTranscript.ts`:

- `finalizeMainStreetTranscript(result)` — finalizes the global recorder's
transcript and returns it (or null if no recorder is set).
- `getMainStreetTranscript()` — returns the current (possibly un-finalized)
transcript from the global recorder.

## API Stability

No public APIs were renamed or removed. All existing imports continue to work.
The only change is the addition of the consolidated `transcript/` sub-module.

## Tests

- All existing tests continue to pass (the re-export shims preserve behavior).
- A new integration test was added:
`tests/main-street/transcript-autosave.integration.test.ts`
This exercises autosave, save/load round-trips, and the consolidated barrel.
8 changes: 5 additions & 3 deletions example-games/beleaguered-castle/GameTranscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,11 @@ import type {
BCMove,
} from './BeleagueredCastleState';
import { FOUNDATION_COUNT, TABLEAU_COUNT } from './BeleagueredCastleState';
import { snapshotCard } from '../../src/core-engine/TranscriptTypes';
import type { CardSnapshot } from '../../src/core-engine/TranscriptTypes';
import { TranscriptRecorderBase } from '../../src/core-engine/TranscriptRecorder';
import {
snapshotCard,
TranscriptRecorderBase,
type CardSnapshot,
} from '../../src/core-engine/transcript';

// Re-export so existing consumers that import from this module still work.
export { snapshotCard };
Expand Down
2 changes: 1 addition & 1 deletion example-games/feudalism/GameTranscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
* @module
*/

import { TranscriptRecorderBase } from '../../src/core-engine/TranscriptRecorder';
import { TranscriptRecorderBase } from '../../src/core-engine/transcript';
import type { DevelopmentCard, PatronTile, ResourceTokens, Tier } from './FeudalismCards';
import type {
FeudalismSession,
Expand Down
3 changes: 1 addition & 2 deletions example-games/feudalism/scenes/FeudalismOverlayManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,7 @@ import { tierDisplayName, resourceDisplayName, formatCost } from '../FeudalismCa
import type { FeudalismSession } from '../FeudalismGame';
import { getInfluence, getWinnerIndex } from '../FeudalismGame';
import { FeudalismTranscriptRecorder } from '../GameTranscript';
import { autoSaveTranscript } from '../../../src/core-engine/autoSaveTranscript';
import { TranscriptStore } from '../../../src/core-engine/TranscriptStore';
import { autoSaveTranscript, TranscriptStore } from '../../../src/core-engine/transcript';
import {
GAME_W, GAME_H, FONT_FAMILY,
createOverlayBackground, createOverlayButton, createOverlayMenuButton,
Expand Down
8 changes: 5 additions & 3 deletions example-games/golf/GameTranscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,11 @@ import type { GolfGrid } from './GolfGrid';
import type { DrawSource, GolfMove } from './GolfRules';
import type { GolfSession, TurnResult } from './GolfGame';
import { scoreGrid, scoreVisibleCards } from './GolfScoring';
import { snapshotCard } from '../../src/core-engine/TranscriptTypes';
import type { CardSnapshot } from '../../src/core-engine/TranscriptTypes';
import { TranscriptRecorderBase } from '../../src/core-engine/TranscriptRecorder';
import {
snapshotCard,
TranscriptRecorderBase,
type CardSnapshot,
} from '../../src/core-engine/transcript';

// Re-export so existing consumers that import from this module still work.
export { snapshotCard };
Expand Down
3 changes: 1 addition & 2 deletions example-games/golf/scenes/GolfOverlayManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,7 @@


import type { TranscriptRecorder } from '../GameTranscript';
import { TranscriptStore } from '../../../src/core-engine/TranscriptStore';
import { autoSaveTranscript } from '../../../src/core-engine/autoSaveTranscript';
import { TranscriptStore, autoSaveTranscript } from '../../../src/core-engine/transcript';
import type { SoundManager, GameEventEmitter } from '../../../src/core-engine';
import {
GAME_W, GAME_H, FONT_FAMILY,
Expand Down
2 changes: 1 addition & 1 deletion example-games/lost-cities/GameTranscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ import type {
import type {
LostCitiesAction,
} from './LostCitiesRules';
import { TranscriptRecorderBase } from '../../src/core-engine/TranscriptRecorder';
import { TranscriptRecorderBase } from '../../src/core-engine/transcript';

// ── Card snapshot ──────────────────────────────────────────

Expand Down
3 changes: 1 addition & 2 deletions example-games/lost-cities/scenes/LostCitiesOverlayManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,7 @@ import Phaser from 'phaser';
import { EXPEDITION_COLORS } from '../LostCitiesCards';
import type { LostCitiesSession, RoundScoreResult } from '../LostCitiesGame';
import { getMatchWinner } from '../LostCitiesGame';
import { autoSaveTranscript } from '../../../src/core-engine/autoSaveTranscript';
import { TranscriptStore } from '../../../src/core-engine/TranscriptStore';
import { autoSaveTranscript, TranscriptStore } from '../../../src/core-engine/transcript';
import {
GAME_W,
GAME_H,
Expand Down
27 changes: 26 additions & 1 deletion example-games/main-street/MainStreetTranscript.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { TranscriptRecorderBase } from '../../src/core-engine/TranscriptRecorder';
import { TranscriptRecorderBase } from '../../src/core-engine/transcript';

// Minimal transcript event types for Main Street
export type PlayerActionDescriptor = { type: string; [k: string]: any };
Expand Down Expand Up @@ -64,3 +64,28 @@ export function recordMainStreetEvent(e: MainStreetTranscriptEvent): void {
// defensive: do not throw from recorder in non-critical paths
}
}

/**
* Finalize the global transcript and return it.
*
* Returns null if no recorder has been set (e.g. in headless tests).
* The `result` parameter should be the game-end result object containing
* at least `gameResult` and `finalScore`.
*/
export function finalizeMainStreetTranscript(result: {
gameResult: string;
finalScore: number;
[k: string]: unknown;
}): MainStreetTranscript | null {
if (!globalRecorder) return null;
return globalRecorder.finalize(result);
}

/**
* Get the current (possibly un-finalized) transcript from the global recorder.
*
* Returns null if no recorder has been set.
*/
export function getMainStreetTranscript(): MainStreetTranscript | null {
return globalRecorder?.getTranscript() ?? null;
}
13 changes: 12 additions & 1 deletion example-games/main-street/scenes/MainStreetTurnController.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ import {
} from '../MainStreetMarket';
import type { BusinessCard, EventCard, UpgradeCard } from '../MainStreetCards';
import { BuyBusinessCommand, BuyUpgradeCommand, BuyEventCommand, PlayEventCommand, BuyRefreshInvestmentsCommand } from '../MainStreetCommands';
import { recordMainStreetEvent } from '../MainStreetTranscript';
import { recordMainStreetEvent, finalizeMainStreetTranscript } from '../MainStreetTranscript';
import { TranscriptStore, autoSaveTranscript } from '../../../src/core-engine/transcript';
import { FONT_FAMILY, createOverlayBackground, createOverlayButton, dismissOverlay } from '../../../src/ui';

export class MainStreetTurnController {
Expand Down Expand Up @@ -80,6 +81,16 @@ export class MainStreetTurnController {

// Update campaign progress (tier evaluation + persistence),
// then compute newly unlocked tiers and show the overlay.
// Auto-save transcript to browser storage (fire-and-forget)
const transcript = finalizeMainStreetTranscript({
gameResult: result.gameResult,
finalScore: result.finalScore,
});
if (transcript) {
const transcriptStore = new TranscriptStore();
autoSaveTranscript(transcriptStore, 'main-street', transcript, '[MainStreet]');
}

s.updateCampaignProgress().then(() => {
const tiersAfter = s.campaign
? s.campaign.unlockedTiers
Expand Down
2 changes: 1 addition & 1 deletion example-games/sushi-go/GameTranscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@
* @module
*/

import { TranscriptRecorderBase } from '../../src/core-engine/TranscriptRecorder';
import { TranscriptRecorderBase } from '../../src/core-engine/transcript';
import type { SushiGoCard } from './SushiGoCards';
import type { SushiGoSession, PickAction, RoundResult } from './SushiGoGame';

Expand Down
3 changes: 1 addition & 2 deletions example-games/sushi-go/scenes/SushiGoOverlayManager.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,7 @@ import type { SushiGoSession, RoundResult } from '../SushiGoGame';
import { getWinnerIndex } from '../SushiGoGame';
import type { SoundManager, GameEventEmitter } from '../../../src/core-engine';
import { SushiGoTranscriptRecorder } from '../GameTranscript';
import { TranscriptStore } from '../../../src/core-engine/TranscriptStore';
import { autoSaveTranscript } from '../../../src/core-engine/autoSaveTranscript';
import { TranscriptStore, autoSaveTranscript } from '../../../src/core-engine/transcript';
import { SFX_KEYS } from './SushiGoConstants';

const transcriptStore = new TranscriptStore();
Expand Down
2 changes: 1 addition & 1 deletion example-games/the-mind/GameTranscript.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@
* @module
*/

import { TranscriptRecorderBase } from '../../src/core-engine/TranscriptRecorder';
import { TranscriptRecorderBase } from '../../src/core-engine/transcript';
import type { PlayerId } from './TheMindGameState';

// ---------------------------------------------------------------------------
Expand Down
Loading
Loading