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>
Specifications
These documents are written for an implementer that reads carefully and lifts code, which today means Claude and
whoever reviews its pull requests. They are not the human summary; that is ../Design.md. Read the
summary first, then Architecture.md, then the document for the system you are touching.
Each spec is pseudocode plus the reasoning behind it. The pseudocode is C++-shaped Unreal code and it is meant to be
implemented, not admired: a class skeleton here is the class we expect to find in Source/, with the same name, the
same members and the same comments about why. Where a spec says // pure, that is a promise about testability the
real code is expected to keep.
Documents
| Doc | Owns | Read when |
|---|---|---|
| Architecture.md | Modules, lifetimes, authority, data assets, gameplay tags, the C++/Blueprint boundary, testing, conventions | Before writing any code |
| Stats.md | The one stat block every body carries, effects and their sources (abilities, areas, surfaces, items, the world), stacking, counters | Step 5, and anything that changes a number on a body |
| Movement.md | The character, the movement component, the look model, camera modes, input, the gym level | Steps 3 to 5 |
| Interaction.md | The one interaction system every prop uses, carrying, throwing | Step 6, and any prop |
| Combat.md | Attributes, the one damage funnel, melee hit detection, enemies, abilities, kits, downed and revive | Steps 7 to 11 |
| Crafting.md | Families, parts, pieces, traits, substances, the pure rules, stations, activities, the link back to combat | Steps 12 to 15 |
| Networking.md | The authority model, the responsiveness tiers, prediction posture, persistence and identity seams | Any replicated feature |
| Telemetry.md | The sink, the envelope, the event catalogue | Any feature (every feature emits) |
| UI.md | HUD, prompts, the theme asset, world-space text, localisation | Any screen or prop text |
Notation
Server*(...) runs on the authority only. Validates the instigator and re-checks every precondition,
then mutates. A UFUNCTION(Server, Reliable) RPC or a plain method guarded by HasAuthority().
Get* / Preview* side-effect-free query. Safe to call from UI every frame, on any peer.
// pure deterministic, no world access, unit-tested, reused by the client preview and the server verdict.
On* (delegate) a multicast delegate a service raises. UI subscribes; UI never polls.
*_Cosmetic a BlueprintAssignable hook a designer may wire. Never load-bearing.
UFooDefinition a UPrimaryDataAsset, authored content.
FFooInstance runtime state with identity (an FGuid).
FFooRecord the persisted snapshot of an instance. Separate type from the instance on purpose.
TAG_Foo_Bar a native gameplay tag, "Foo.Bar", declared once in code. Never a string literal at a call site.
Status markers inside a spec:
[DECIDED] settled, with the reason. Logged in ../Decisions.md.
[PROPOSED] the recommended shape, not yet built on. Becomes [DECIDED] when the step that builds it closes.
[SALVAGED] an idea carried over from the two earlier projects, reshaped for this one. See ../Ideas.md.
Qn an open question, collected at the end of each document.
Cross-cutting rules
Every spec obeys these. They are stated once here so no spec has to restate them, and any spec that appears to break one is wrong.
- The server decides. The client asks, and predicts only its own body. Every mutation has an explicit instigator and is re-validated on the authority, even when the game is being played alone. Standalone and listen-server play are development conveniences; the dedicated server is the real target and nothing may assume it is absent. See Networking.md.
- One formula, many consumers. A preview and the real thing call the same pure function. Damage, coherence, naming, assembly validation, spec matching and interaction prompts all follow this. If a UI-side "fast copy" of a rule ever appears, it is a bug.
- One funnel per kind of consequence. All damage goes through one execution. All interactions go through one service. All activity quality goes through one result type. A second path is where rules quietly diverge.
- Rules are plain C++ in the core module; actors and components adapt them to the world. The core module knows
no
AActor. This is what makes rules testable without a level and runnable on a headless server. - Content is data, addressed by gameplay tag or primary asset id, never by string. A new sword, enemy or substance is an asset, not a code change. If adding a variant needs code, the model is wrong.
- Every rejection carries a reason a player can read. A refused interaction, an illegal assembly, a blocked ability: each returns a reason tag that the prompt or panel shows. Silent refusal is a failed step.
- Nothing is destroyed silently. Dropped things persist on the floor, full containers refuse with a reason, thrown things land where they land. The rare deliberate deletion is announced before it happens.
- Bonuses, never locks. A class, a level or a piece of gear changes speed, quality or numbers. It never changes what a station accepts or what an assembly permits, and it never grants another class's signature ability.
- Solo viability is a gate, not a mode. Every rule is checked against one player. Where it costs a solo player something, the spec says so and names the tuning knob.
- Derived state is recomputed on load, never trusted from storage. A rebalance re-scores old items everywhere.
- Telemetry from the first line. Every feature emits through the injected sink into a catalogue that exists before there is anyone to measure. Retrofitting emit calls is the tax this avoids.
- Determinism where it is promised. Anything generated from a seed uses its own
FRandomStream, integer coordinates and ordered collections, and never reads physics, time or frame counts. Nothing today is generated from a seed; the rule exists so the first thing that is inherits it. - One stat block; every outside influence is an effect on it. Every body carries the same attribute set. A buff, a debuff, ground, weight, gear and base values are all gameplay effects applied to it; counters are tags and attributes on the receiver. No system keeps its own multiplier. See Stats.md.
How a spec relates to the steps
../Steps.md schedules; these documents specify. A step links to the sections it builds and states
what proves it done. If a step and its spec disagree, the spec wins and the step is corrected. A spec that turns out
wrong in the building is corrected in the same pull request as the code, under a What was built, and where it
differs heading at the end of the affected document, so the next reader is not misled by the sketch.