# Ultima V port staging area

This directory contains Ultimatum-owned planning, reproducibility metadata, and the web-first integration slice for the proposed Ultima V source port. See [`ROADMAP.md`](ROADMAP.md) for current status, known gaps, and next milestones.

There is no upstream engine source, original game data, generated game asset, production route, or deployable bundle here. `upstream.lock.json` identifies the exact Ultimatum-maintained OpenU5 fork revision used for the slice. Ultimatum's bridge is committed in that fork, which is the single source for it; this repository keeps no second copy. The lock is verified and re-pinned with `node tools/engine-lock.mjs` (see [`ULTIMATUM_ENGINE_ACQUISITION_POLICY.md`](../../docs/architecture/ULTIMATUM_ENGINE_ACQUISITION_POLICY.md)).

Original Ultima V data stays user-supplied and ignored. Don't add `ULTIMA.EXE`, `SAVED.GAM`, overlays, maps, fonts, tiles, extracted JSON, screenshots, audio, or test saves to this directory.

## Reproduce the working slice

1. Clone the pinned fork (see `repository` in `upstream.lock.json`) and check out its `commit`.
2. Install its locked dependencies with a supported Node release.
3. Run the focused test from `<checkout>/game`: `vitest run tests/ultimatum-session.test.ts`.
4. Run the OpenU5 extractor against a user-supplied Ultima V installation outside this repository, then start its development server.

The bridge is exposed as `window.__ultimatumU5` and currently provides revisioned snapshots, sequenced semantic actions, validated full-state checkpoints, restore through OpenU5's canonical load path, pause/quiesce/resume, and shutdown. `platform/web-engine-session.js` is the platform-side compatibility wrapper; it serializes calls, rejects stale state, and deduplicates intent IDs. Semantic host actions now enter OpenU5's canonical contextual reducer directly; physical keyboards keep their normal DOM listener, but the host no longer synthesizes `KeyboardEvent`s.

`platform/import-adapter.js` expresses the pinned extractor's 33-file input contract over opaque inventory entries and supplies the shared importer guidance descriptor. `platform/cache-install-store.js` stages extracted assets in an unpublished generation, verifies them, and atomically publishes an active pointer while retaining legacy fallback and rollback. `platform/legacy-storage.js` classifies existing OpenU5 browser state without migrating it. See `docs/engineering/OPENU5_STORAGE_IMPORT_CONTRACT.md`.

`platform/save-store.js` is the U5 local `SaveStore` implementation. It writes only to the platform namespace `ultimatum-save-store-v1`, validates with OpenU5 before publication, checksums bounded payloads, uses compare-and-swap publication, retains current and previous immutable generations per named slot, and reads/writes the generic `.ultsave` v1 envelope. A separate deduplicated recovery slot is captured periodically and on lifecycle transitions. Existing `u5clone:*` saves are merely inventoried until the player explicitly copies one; their source records are never deleted or changed.

These remain proof-of-life seams, not the final production adapter. Game-data installation and full-fidelity local checkpoints are platform-published, and the QA host now owns a capability-driven mobile control deck with semantic movement, contextual command palettes, visible cancel/confirm paths, explicit text entry, and a conversation sheet with a live transcript, discovered-topic assistance, and a searchable journal of recorded dialogue passages. The hosted flag suppresses OpenU5's touch deck so the two surfaces never compete; standalone OpenU5 keeps its controls and save panel unchanged. In Ultimatum, F5 and System → Save/Load route to the platform drawer. Control profiles/remapping, hold-to-move, cloud synchronization, and native hosts remain later work.

The QA-only `platform-host/` is the first platform-owned launcher. It resolves either a published Ultimatum generation or an existing OpenU5 extraction and otherwise directs the browser to the shared setup/import surface. The importer reuses Ultimatum's guidance, diagnostics, review, and confirmation component; OpenU5 supplies byte collection and parsing. The host embeds the same-origin game runtime through `OpenU5WebEngineSession`; its Data & saves drawer is platform-owned and orchestrates checkpoints, rollback, `.ultsave` portability, and explicit legacy copying without putting platform persistence inside OpenU5.

For real-engine verification without copying private generated assets into a build, `tests/serve-platform-host.py --checkout <pinned-checkout>` serves the host and that checkout's ignored `game/dist/assets` on loopback only. Its in-memory response transform bypasses only the installed-data gate; packaged host code is unchanged. `node ports/ultima5/scripts/qa-host.mjs --source <Ultima V DOS folder>` is the one-command wrapper: it builds the pinned checkout if needed, extracts game data when `game/dist/assets` is missing (builds wipe it), and launches the harness with the debug URL printed.
