# OpenFUT — Project State

> **Canonical source:** `../OpenFUT-Vault/06 Agent Memory/Project State.md`
> This file is a mirror. If the two disagree, the vault wins. Update the vault first.

Factual snapshot. Prefer this over the stale root `README.md`/`CLAUDE.md` status tables (FIFA 23).
Last compiled from repository evidence during context initialization.

## Working

- **FIFA 17 offline FUT stack, end-to-end.** Proven 2026-08-01: auth → Blaze login → device-trust →
  the FUT hub. Brought up by `fifa17-recon/tools/openfut-fut.sh start`. Evidence: `FUT-RUNBOOK.md`,
  `fifa17-recon/README.md`, the five responder scripts, gate-ladder troubleshooting table.
- **ProtoSSL cert-pin defeat** — two live `/proc/PID/mem` patches (`autopatch.py`), VAs stable
  across launches. gdb-verified which gate was the wall.
- **LSX / Origin layer** — crypto handshake reversed byte-exact and confirmed against the repack's
  own emu disassembly (`docs/REPACK_INTEL.md`); Origin login gates cleared.
- **Blaze redirector + Fire2/Heat2** — both hops defeated; preAuth/login/personas answered.
- **UTAS/RS4 FUT API** — `ut/auth` + boot calls + device-trust reach the hub with hand-authored JSON.
- **Persistent FIFA 17 account selection** — the launcher synchronizes one configured EA persona to
  the Python backend before starting LSX/FIFA. LSX, Blaze, POW/EASFC, and UTAS then share that
  identity, while FUT coins, inventory, squads, progression, and unopened packs persist in an
  isolated save beneath `fifa17-recon/docker/state/accounts/<persona-id>/`. The POW level/XP/funds
  shown in FIFA's general account bar are account-scoped but remain distinct from FUT club coins.
  A reversible server test on 2026-08-09 verified profile switching, POW values, a 400-coin pack
  debit, five awarded items, and restoration of the original profile.
- **Account-scoped FUT security compatibility** — launcher account synchronization initializes a
  persisted `securityQuestion` verification record in that persona's FIFA 17 profile. The UTAS
  PHISHING handler returns the complete CardsDLL trusted-console response (`changed`, `exists`,
  `locked`, `trusted`), accepts only well-formed legacy setup/validate requests under `X-UT-SID`,
  and never stores or logs the client-transformed answer. This is server-side emulation; the hook
  and launcher do not contain an answer or add UI automation. Automated contract coverage is in
  `fifa17-recon/docker/ctx/tools/test_security_question.py`; live first/repeat-launch acceptance is
  partially complete: the first launch entered FUT without a security dialog on 2026-08-09; a
  second fresh-process FUT entry is still required to close persistence acceptance.
- **Safe responder diagnostics** — ordinary LSX logs redact challenge/session/auth-code attributes;
  ordinary Blaze logs redact auth/session keys and no longer emit raw Fire2 hex, decoded TDF, or
  config values. Forensic Blaze capture remains available only with the explicit
  `OPENFUT_BLAZE_DUMP_FRAMES=1` opt-in. LSX and Blaze self-tests cover the new defaults.
- **OpenFUT Core** — Rust FUT economy backend, feature-complete for its scope and tested: profiles,
  clubs, coins, packs, cards, squads, chemistry styles, SBCs, objectives, matches, market (NPC),
  draft, FUT Champs, seasons, statistics, achievements, events, daily check-in, division
  leaderboard, market trade history. 13 migrations. Integration suite (`tests/integration_test.rs`,
  96 test fns) runs against in-memory SQLite; CI (fmt/clippy/build/test) green on `openfut-core`.

## Partially implemented

- **FIFA 17 FUT hub depth** — reaching the hub is proven, but how much of FUT is fully navigable
  beyond it (playing matches, pack opening, SBC submission through the *game* UI vs. spinner/error
  states) is not documented as complete. The runbook's gate ladder lists failure modes still
  guarded against. Treat "past the hub" as unverified.
- **Pack opening through the game UI** — proven live on 2026-08-09 with the recovered CAGE test
  profile: purchase, reveal, item assignment/quick-sell, wallet refresh, and return from the reveal
  all completed. The Python transaction path also passes its 446-check contract suite. FIFA's
  hardcoded post-reveal `mypacks` return is supported by a short-lived active grace record for every
  opened pack; it is excluded from unopened-pack counts and retired at the next hub request.
- **FIFA 17 FUT match lifecycle** — CardsDLL static analysis and isolated responder tests now cover
  CREATE→READY→PLAY→END. Bare `/match` requests carrying body `matchId` are classified as PLAY
  instead of accidentally allocating another match; READY returns the verified scalar `matchId`
  and `opponentPersonaId` fields; END persists W/D/L, matches played, and coin rewards per account.
  The implementation is deployed and `test_match_lifecycle.py` passes, but no football match has
  started or completed in FIFA yet. The READY opponent `items` contract and client mode-entry gate
  remain unresolved; `FUT_MODES` therefore stays off by default.
- **Core ↔ emulation integration** — the two halves exist and wiring has **started**. First
  slice (2026-08-11): the My Squad owned-player search. `openfut-core` gained a semantic,
  game-independent owned-inventory query (`services::inventory::{OwnedItemQuery, apply_query}`
  + a `Quality` tier) that filters (AND) → orders deterministically → paginates, wired into
  `GET /collection`; `openfut-adapter-fifa17::fut::owned_query` parses the FIFA17 `/club` wire
  query and resolves numeric league/nation/team ids → semantic names (unknown id = hard error,
  no raw-id passthrough). Intentional fix, not parity: Python applies only `league`+`team` and
  ignores `level`/`rare`/`position`/`nation`/`start`/`count` (the request-amplification bug);
  Core applies all proven filters and paginates. `rare=SP` semantics UNKNOWN, unimplemented.
  Slice 2 (2026-08-11): `openfut-utas-host` — the first live UTAS host. Serves `GET …/club`
  from Core through the adapter and reverse-proxies every other UTAS route verbatim to the
  Python oracle (`utas_server.py`); plaintext HTTP/1.1 keep-alive, classify-before-execute,
  no python-fallback after a Core error. `CoreAccess` is a host-owned boundary (the adapter
  stays transport-agnostic). 11 host + 22 adapter tests; 10/10 mutations killed; fmt/clippy
  clean. Slice 3 (2026-08-11, `3ef3bc3`): the real `Fifa17IdentityResolver` — catalog
  (card id → real asset id) + persistent `openfut-identity` store (owned instance →
  stable/reversible wire int) + wire-id policy, replacing all placeholders (one
  production path). Wire-id namespace is globally monotonic within `(fifa17, owned-item)`,
  not per-account (Core owned ids are UUIDs → unambiguous reverse). Slice 4 (2026-08-11,
  core `36abd4b`): a curated 32-card real FIFA17 dev content pack
  (`data/games/fifa17/dev/cards.json`, ids `fifa17_<asset>`), loaded only via opt-in
  `Config.dev_content_games`; `seed-dev` grants a `game_id=fifa17` profile+club real
  `OwnedCard`s (no FIFA wire ids — the resolver mints those at request time), idempotent,
  default content untouched. Slice 5 (2026-08-11, `5276dd2`): the host sends
  `X-OpenFUT-Game: fifa17` so `/club` resolves the all-mapped fifa17 profile —
  **composition proven live** over HTTP (real Core+host, no FIFA client): FIFA wire query
  → real `resourceId`s (catalog) + stable/reversible wire `id`s (store); 33 renderable,
  gold=22, Premier League=18, pagination page1=11/page2=7/overlap=0 (clean paging, no
  drops). **The only remaining gate is the live retail FIFA A/B** (no FIFA client in the
  build env; runbook `openfut-utas-host/README.md`). See the vault UTAS Endpoint Map +
  Known Issues (incl. "identity resolution is NOT authorization" for later mutations).
  **Slice 6 (2026-08-11): `/club` RUNTIME VALIDATED on retail FIFA 17** — operator-assisted
  live A/B (`.105` client → `.120` backend via a source-scoped NAT redirect into a staged
  `36abd4b` Core + `openfut-utas-host`). Every checkpoint passed on the real client:
  transport, per-route Python fallback, Python-negative `/club`, Core `X-OpenFUT-Game`
  scoping, real card + persistent owned-item identity (incl. the two-copy fixture),
  no-filter/Gold/position/nation/league/team/combined-AND filtering, retail pagination
  with no amplification, card selection, identity stability across relaunch, Python
  rollback, and Rust re-enable (identity store byte-identical across the cycle, 0
  reallocation). Live findings: the retail client steps `start += 10` with `count=11`
  (sometimes bulk `count=100`) — Core honours `offset` and terminates; the My Club UI
  nests team under league; the "~1900" club counter is Python `userMassInfo`, not `/club`.
  Remaining is operational only (promote the staged stack to a durable deployment).
- **Slice 7 (2026-08-12): FUT squad authority (read + write) RUNTIME VALIDATED on retail FIFA 17.**
  Staged operator-assisted A/B (`.105` retail client → `.120`, source-scoped utas switch
  `:8099→:8199` into `openfut-utas-host` over a staged `615c5fd` Core seeded by `seed-dev` with the
  dev 33-card inventory). FIFA itself consumed the Rust squad path end-to-end: FUT boot served
  `RUST_OVERLAY userMassInfo` (squad overlay) and the squad screen rendered the dev XI; a controlled
  in-game squad edit issued `PUT …/squad/0` → host `squad-replace` → Core → `{"id":0}`; an in-game
  formation change f442→f433 persisted to Core (canonical fingerprint changed, `position_index`
  remapped 0–10); a FULL FIFA relaunch cold-fetched and rendered the persisted f433 squad (no client
  cache). Reversibility proven on the exact client path by log presence, not response data (both
  backends coincidentally hold the same dev squad — the Python oracle `fifa17_profile.json` was
  seeded from the same `squad_put_f442.json` capture): disarmed `.105:8099` reached Python (absent
  from host log), re-armed reached Rust (`route=club limit=Some(9)` present); the persistent identity
  store survived the cycle with 0 reallocation. Non-migrated routes (`/ut/auth`, `account/sync`,
  `accountinfo`, `settings`, `hub`, store txn) correctly Python-fallback. NEW startup requirement:
  the Core server loads dev card defs only for games in env `OPENFUT_DEV_CONTENT_GAMES` (comma-sep);
  omitting `fifa17` makes `get_collection` silently drop every owned card (empty `/collection`,
  "no squad") despite a successful seed — MUST become a deployment/preflight assertion. Preceded the
  same day by a staged two-process parity gate (real Core+host over HTTP, no FIFA) confirming byte-
  shape parity of `userMassInfo.squad` vs the captured oracle. Next milestone: real-data Core
  import / profile strategy → production Rust UTAS. Blaze deferred.
- **Slice 8 (2026-08-12): REAL-DATA staged retail A/B PASS (import + club + squad).** The real FIFA 17
  profile was imported into a staged Core via the two-store protocol (`openfut-import-fifa17 --apply`:
  generic transactional Core import + `openfut-identity` seeding, idempotent), then FIFA itself
  consumed it end-to-end over the source-scoped utas switch (`.105`→`.120:8199`). Imported population
  1949 of 1962 player instances (13 Legend/special assets deferred as unnameable — 9 NoName + the 4
  copies of `169193`), every original Python wire id preserved (set-equal, 0 minted/dropped), each
  owned instance an opaque Core UUID. On the retail client: real club rendered (1949, not the 33-card
  dev XI); `/club` pagination clean (paged==full, no loop/overlap/drop); the `Special` quality filter
  returned only specials (1665, 0 base leaked) and paginated the filtered set; a squad edit persisted
  to Core (canonical + opaque extension atomically, fingerprint recomputed) and survived a full cold
  relaunch; rollback→Python→Rust re-enable proven on the exact client path (disarm reached Python,
  re-arm returned the imported club + edited squad; identity store 0 reallocation). Three real-data
  fidelity gaps the base-only dev fixture had hidden were found on the live client and fixed:
  (1) `e187cd4` versioned `resourceId` — `shape_item` emitted the base assetId as `resourceId`,
  collapsing every special onto its base card art; `Fifa17Identity` now carries the versioned
  `resource_id`. (2) `626c972` observed `rareflag` — `shape_item` hardcoded `rareflag=1`, so all
  specials rendered as basic rare; `rareflag` now flows through the FIFA catalog and onto the wire
  (wire distribution == source exactly). (3) `6f16a23` `rare=SP` Special filter — previously a no-op
  ("semantics UNKNOWN"), now grounded as `rareflag > 1` and applied host-side (Core has no rareflag).
  Also `44fcf24`: nation/league/team proven to be INSTANCE metadata (not definition identity — the 4
  copies of `169193` are identical but for club), so the definition-consistency gate now compares
  identity only (no `--defer-conflict` needed; no majority-vote). PRODUCTION NOT READY: the 13 deferred
  Legends are unnameable from the `.120` client dump (absent/placeholder in `players.json`; DLC/Legends
  name tables empty) — honest names require the FIFA 17 Legends locale data from the `.105` client.
  Next: resolve the 13 names → re-import to 1962/0 → full-fidelity gate → gitlink reconcile →
  production Rust UTAS.

## Stubbed / planned

- **`fifa-blaze`** (Rust) — Milestone 1 capture stub only. Two TLS listeners that log packets; no
  FIFA 23 component/command handlers. Its own README says IDs are unknown. Superseded in practice by
  the Python FIFA 17 responders, kept as the intended FIFA 23 implementation surface.
- **`openfut-launcher` legacy controls** — core/bridge and FIFA 23 setup controls belong to a
  superseded plan. The launcher now also owns the live FIFA 17 client flow: server/hook config,
  account synchronization, local LSX, privileged autopatch, and game launch.
- **`tools/`** (file-watch-diff, squad-injector, exporters) — helpers for the FLE-Lua-bridge idea in
  `docs/direction.md`. Not part of the live FIFA 17 path.
- **`docs/foundational-xi-injection-test.md`** — a planned (not executed) test procedure for the FLE
  bridge route.

## Stubbed / blocked (FIFA 23 lineage)

- **`openfut-bridge`** — in-process `version.dll` hook on ProtoSSL. Git history: injection works but
  the effort hit an "architectural wall" (async event-driven gate, not a poll). Superseded first by
  the FLE-bridge pivot, then by the FIFA 17 route. Its `CLAUDE.md` task list is historical.

## Unknown / requires investigation

- Whether the FIFA 17 hub supports actually **playing a FUT match** offline and getting results back.
- Whether FUT actions beyond the now-verified pack reveal/assignment flow (submit SBC, transfer
  market buy/sell, matches) round-trip correctly through `utas_server.py`.
- The exact division of FUT state ownership once Core is wired in (who is source of truth).
- Degree of FIFA 23 wire-format identity — asserted ("identical wire format") but the FIFA 23 client
  has not been re-tested against these responders in this repo's evidence.

## Known technical debt / hazards

- **Root docs are stale.** `README.md`, `CLAUDE.md`, `openfut-bridge/CLAUDE.md` all describe FIFA 23
  as the target and mark FIFA 23 integration as the open item — they predate the FIFA 17 success.
- **All host state is volatile** across reboot except the `/etc/hosts` line — re-run
  `openfut-fut.sh start`. Requires `ptrace_scope=0` + root arming (security-relevant).
- **Whole stack rides on EAAC staying neutralized** and game updates being off; a client update can
  break the memory patches (VAs) and cert bypass.
- **`fifa17-recon/tools/lsx_responder_v2.py` is currently modified in the working tree** (uncommitted).
- `33068179` / `CAGE` remains the responder fallback, but the launcher now blocks one-button launch
  until an explicit persona is configured and synchronized across LSX, Blaze, POW, and UTAS.
