Skip to content

feat: XNet ↔ Unreal Engine 6 interop — game schema pack + governed connector (0200) - #186

Merged
crs48 merged 4 commits into
mainfrom
claude/unreal-interop-impl
Jun 18, 2026
Merged

feat: XNet ↔ Unreal Engine 6 interop — game schema pack + governed connector (0200)#186
crs48 merged 4 commits into
mainfrom
claude/unreal-interop-impl

Conversation

@crs48

@crs48 crs48 commented Jun 18, 2026

Copy link
Copy Markdown
Owner

Implements the keystone (Phase 2) of exploration 0200: the XNet half of the XNet ↔ Unreal Engine 6 bridge — the governed, user-owned, cross-game data plane UE6's portability vision implies but doesn't itself supply.

What ships

1. Game-interop schema pack (@xnetjs/data, packages/data/src/schema/schemas/game.ts)
The durable, save-file-grade vocabulary that flows between an engine and XNet:
PlayerIdentity (keyed to the player's portable did), Inventory, GameItem, Achievement, MatchSession, GameEconomyEntry, GameAsset. Every node is space-scoped (spaceCascadeAuthorization()) so a title sees only its own game/* space — never the player's finance/CRM/notes. Assets are glTF/USD file() refs (XNet stores the CID, never the mesh); economy is money() minor units (a ledger fact, not a payment rail). Registered through the 3-layer barrel; passes authorization-coverage.test.ts.

2. @xnetjs/unreal connector package (new, server-side)

  • buildUnrealConnector → a real defineConnector-valid ConnectorDefinition (fyi.xnet.connector.unreal): a game is "just another external service" (per 0196). Capability-scoped secrets/schemaWrite/network, opt-in agentTools, a pull that maps events → nodes. A test wraps it with the real defineConnector to prove zero structural drift.
  • Granularity guardrail — makes 0200's central rule executable: save-file data may sync, netcode-packet state may not. assertDurableCadence rejects any cadence below a 1s floor; assertDurableSchemas rejects non-durable targets — at build time.
  • mapGameEventToNode — pure event → node translation; soft currencies map to a money() value with ISO XXX.

Tests

  • packages/data unit: 1476 pass (incl. new game.test.ts + authorization-coverage).
  • @xnetjs/unreal integration: 27 pass (granularity, event mapping, connector incl. real-defineConnector compatibility).
  • tsc --noEmit clean; eslint + format:check clean.

Deferred (tracked as unchecked in the 0200 doc)

Hub connectorSyncFeature mount, the "Games"/guild-manager UI surfaces, the UE-side client SDK (separate repo), Phase-3 runtime UCAN auth, and Phase-4 DID-login. The connector ships unmounted — exactly like the 0196 connectors — to be wired when a real title API exists. No 3D engine or heavy dependency lands in the web/desktop bundles.

🤖 Generated with Claude Code

xNet Test and others added 3 commits June 17, 2026 19:55
Add the PlayerIdentity / Inventory / GameItem / Achievement / MatchSession /
GameEconomyEntry / GameAsset schemas — the durable, save-file-grade vocabulary
that flows between a game engine (Unreal Engine 6) and XNet. Every node is
space-scoped (spaceCascadeAuthorization) so a title sees only its own game/*
space; assets are glTF/USD file refs (XNet stores the CID, never the mesh);
economy is money() minor units (a ledger fact, not a payment rail).

Registered in builtInSchemas + the 3-layer barrel; passes authorization-coverage.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…on 0200)

New package implementing the XNet ↔ Unreal Engine 6 bridge's XNet half:

  - buildUnrealConnector — a @xnetjs/plugins ConnectorDefinition that pulls a
    title's normalized event stream into governed, space-scoped nodes (a game is
    "just another external service", per exploration 0196). Capability-scoped:
    secrets held by the hub broker, schemaWrite limited to the game pack, network
    limited to the title's host.
  - granularity guardrail — assertDurableCadence/assertDurableSchemas enforce the
    central rule of 0200: save-file-grade data may sync, netcode-packet state may
    not. Rejects any cadence below a 1s floor and any non-durable schema target.
  - mapGameEventToNode — pure event → node translation; soft currencies map to a
    money() value with ISO 'XXX' (name kept in the text field).

Tests run in the integration project; includes a real defineConnector
compatibility check so there is no structural drift from the connector primitive.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
…nector, granularity guard)

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@crs48
crs48 temporarily deployed to pr-186 June 18, 2026 02:56 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

✓ Changelog fragment found — thanks!

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@crs48
crs48 temporarily deployed to pr-186 June 18, 2026 02:57 — with GitHub Actions Inactive
@github-actions

github-actions Bot commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Preview removed for PR #186.

github-actions Bot added a commit that referenced this pull request Jun 18, 2026
@crs48
crs48 merged commit 470f22e into main Jun 18, 2026
12 checks passed
@crs48
crs48 deleted the claude/unreal-interop-impl branch June 18, 2026 03:05
github-actions Bot added a commit that referenced this pull request Jun 18, 2026
crs48 added a commit that referenced this pull request Jun 18, 2026
## Summary

Implements exploration **0202** — makes changelog PR-number resolution
**robust and visible** without adding a second commit. (Supersedes the
doc-only PR #190, which is folded in here.)

The investigation found PR linking *wasn't* actually broken — the
deploy-time `resolve-prs.mjs` works — but it was fragile (regex over
merge-commit subjects), silent on failure, and entries without
screenshots looked bare. This hardens all three.

### Robustness — `resolve-prs.mjs` + deploy
- **Authoritative API fallback:** when the git-subject parse misses,
resolve via the GitHub `commits/{addSha}/pulls` API (keyed by the
fragment's adding commit) — **merge-method-proof**, so rebase-merges
resolve too. Verified live: `commits/4f413f61/pulls → #187`.
- **Fail-loud:** unresolved fragments now emit a `::warning` annotation
+ summary line instead of silently shipping numberless. Never blocks the
deploy (entries still render).
- `deploy-site.yml` passes `GITHUB_TOKEN` to the step.

### Authoring — `new.mjs`
- **`--pr auto`** (default) bakes the PR number via `gh pr view` when
the branch already has a PR — one commit, visible in the
repo/previews/local. Omitted when no PR exists yet → the deploy resolves
it. (`--pr <N>` / `--pr none` override.) **This fragment was added that
way.**
- Fixes the **UTC-tomorrow date drift** (stamps the local day).

### UI — `index.astro`
- A **"View PR #N →"** affordance for entries that have a PR but no
gallery, so server/docs changes don't read as broken. (Built HTML shows
it for #187/#186/#184…)

### Notably *not* done (a finding, not a gap)
- The exploration suggested running resolve in the preview workflows.
They build only `apps/web`, **not** the Astro `site/`, so the changelog
page is never in previews — running resolve there would be dead code.
Skipped to avoid useless pipeline surface.

## Testing
- `resolve-prs.mjs` resolves all current slug entries via git; API
fallback verified live against the real endpoint.
- `new.mjs`: local-date + graceful no-PR + bake-on-`--pr` all
smoke-tested.
- `site/` builds (70 pages); "View PR #187" affordance present in the
built changelog HTML.
- Not eslint/prettier-gated (lint covers `packages apps`; `format:check`
skips `scripts/site`), but formatted and syntax-checked anyway.

Exploration:
`docs/explorations/0202_..._CHANGELOG_PR_NUMBER_RESOLUTION_ROBUSTNESS.md`
(items checked off).

🤖 Generated with [Claude Code](https://claude.com/claude-code)
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