Step 2: the telemetry seam

ITelemetrySink with the null, log and JSON Lines sinks, FTelemetryEvent and the envelope, TelemetryEvents,
UTelemetrySubsystem on the game instance, ASaltyGameMode minting the session id into the replicated
ASaltyGameState, bs.TelemetryTest, the git hash in the build string (D-42) and four Salty.Core.Telemetry tests
replacing the placeholder. Proved standalone and with a headless server plus client sharing one session id.

Also enables the engine's Editor, AutomationTest, GameplayTags, ConfigSettings and LiveCoding MCP toolsets
(D-43) so the editor's MCP server exposes more than the skills toolset.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
This commit is contained in:
Rainer Leit
2026-09-16 20:22:20 +03:00
co-authored by Claude Fable 5.1
parent 4f2c55cd2a
commit 3e52d26fb7
25 changed files with 881 additions and 23 deletions
+7
View File
@@ -7,6 +7,13 @@ say so.
Status: `Decided` · `Supersedes` (contradicts an earlier decision, names it) · `Deferred` (deliberately not now,
with a trigger)
## 2026-09-16 — Step 2, the telemetry seam
| # | Decision | Where |
| --- | --- | --- |
| D-43 | **The editor serves the engine's MCP plugin on `127.0.0.1:8000/mcp` whenever it is open.** `bAutoStartServer` in `Config/DefaultEditorPerProjectUserSettings.ini`, the port `.mcp.json` points Claude Code at, plus the `EditorToolset`, `AutomationTestToolset`, `GameplayTagsToolset`, `ConfigSettingsToolset` and `LiveCodingToolset` plugins. Loopback only; nothing listens when the editor is closed. Everything the plugin sends to an LLM is Licensed Technology under the UE EULA §6(e). | [CLAUDE.md](../CLAUDE.md) |
| D-42 | **The build string is `FApp::GetBuildVersion()` plus the git short hash read by `Salty.Build.cs` at build time.** A commit changes the definition and rebuilds the gameplay module; that cost is accepted so a telemetry file never lies about which build wrote it. `nogit` when git is unavailable. | [Telemetry](Spec/Telemetry.md) |
## 2026-09-16 — Step 1 closes the two open decisions
Taken when the project was created from the engine's Third Person template.
+27
View File
@@ -178,3 +178,30 @@ does not.
settings pass, not before.
- **Q4. Typed payloads.** A JSON object per event is convenient and unsafe. A typed struct per event name is the
later answer if typos in payload keys start costing analysis time.
## What was built, and where it differs
Step 2, 2026-09-16.
- **Layout as specified.** `SaltyCore/Telemetry/` holds `TelemetryEvent.h` (event, envelope, the `FTelemetryPayload`
builder and the pure `Telemetry::ToJsonLine`), `TelemetrySink.h` (the interface and the three sinks) and
`TelemetryEvents.h` (the four session names; the catalogue grows with each emit call). `Salty/Core/` holds
`UTelemetrySubsystem`, `ASaltyGameMode`, `ASaltyGameState` and `SaltyCheats.cpp`.
- **The serialisation is one pure function** the sinks share, so the tests check the envelope without a sink.
The JSON Lines sink is an `FRunnable` draining an MPSC queue every two seconds and on `Flush`; `Emit` only
builds the line. Files are `Saved/Telemetry/session_<utc>_<8 hex>.jsonl`, the hex being a per-instance id so
two PIE instances started in the same second never share a file.
- **Sink choice:** `-telemetry` or `telemetry.File 1` wins everywhere, including the editor, so a PIE session
with a dedicated server writes one file per instance; the log sink is the editor default; null otherwise.
- **The session id** is minted in `ASaltyGameMode::InitGameState`, written to `ASaltyGameState::SessionId`
(replicated, `OnRep` adopts it) and begun on the server directly. `ATemplateGameMode` now derives from
`ASaltyGameMode` so the template Blueprint game mode mints a session until step 3 replaces it.
- **`MarkCheatUsed` takes an optional command name** and puts it in the `cheat_used` payload; the spec's
no-argument signature still works. `bs.TelemetryTest` is that call, so it emits and taints in one.
- **`player_id` is empty on every peer** until step 3 mints one on the player state; `party_size` is the game
state's player count and is zero at `session_started` because the join has not happened yet.
- **The build string** is `FApp::GetBuildVersion()` plus the git short hash read by `Salty.Build.cs` (D-42).
- **Proved:** four `Salty.Core.Telemetry.*` tests; a standalone `-telemetry` run wrote `app_started`,
`session_started`, `session_ended`; a headless `-server` and a `-game` client each wrote a file carrying the
same `session_id` with `is_server` true and false; `bs.TelemetryTest` wrote `cheat_used` and every later line
carried `cheats_used: true`.
+1 -1
View File
@@ -26,7 +26,7 @@ Status: `☐` not started · `◐` in progress · `☑` done · `⊘` dropped ·
| # | Step | Proves | Status |
| --- | --- | --- | --- |
| 1 | The project, two modules, tests, a dedicated server in the editor | It builds, tests run headless, and the server posture is real from the first commit | ◐ |
| 2 | The telemetry seam | Every later step can emit into something | ☐ |
| 2 | The telemetry seam | Every later step can emit into something | ☑ |
| 3 | A body, a camera, an input map and the gym | You can walk, in both camera modes, on a server | ☐ |
| 4 | The feel pass | The written checklist passes with a person at the keyboard | ☐ |
| 5 | The ability foundation and the stat block, proved on dodge, blink and mud | Abilities predict cleanly, and every body has the one set of numbers every effect changes | ☐ |
+8
View File
@@ -5,6 +5,14 @@ the reasoning lives in the specs, this is the memory.
## Done
- 2026-09-16 — Step 2: `ITelemetrySink` with null, log and JSON Lines sinks; `FTelemetryEvent`, the envelope and
`Telemetry::ToJsonLine`; `TelemetryEvents` with the four session names; `UTelemetrySubsystem`; `ASaltyGameMode`
minting the session id into `ASaltyGameState`; `bs.TelemetryTest`; the git hash in the build string (D-42);
four Core tests. Proved standalone, and with a headless `-server` plus `-game` client sharing one session id.
- 2026-09-16 — The editor's MCP: the engine's experimental plugin only listens when `bAutoStartServer` is set, so
`.mcp.json` refused until `DefaultEditorPerProjectUserSettings.ini` set it (D-43). Enabled the Editor,
AutomationTest, GameplayTags, ConfigSettings and LiveCoding toolsets; before that the only toolset was skills.
Reconnect with `/mcp` after the editor is up. Live Coding must be closed before a shell build.
- 2026-09-16 — Step 1: `Salty.uproject` (UE 5.8) at the root from the Third Person template, renamed from
`ProjectSomething` with class redirects; `SaltyCore` and `Salty` modules; `SaltyServer.Target.cs` (unbuildable on
the launcher, OD-04); `USaltyAssetManager` calling `InitGlobalData`; `Config/Tags/` with 23 root namespaces;