This file provides guidance to Claude Code (claude.ai/code) when working with code in this repository.
A C# library for the Rust+ companion app API, shipped as six NuGet packages from src/. All
libraries multi-target netstandard2.0 + net10.0 (usable from .NET Framework 4.6.2+ through
.NET 10); Polyfills/ directories and #if NET10_0_OR_GREATER forks bridge the gap. Package
versions are managed centrally in Directory.Packages.props. The default branch for PRs is
develop.
dotnet build # strict: TreatWarningsAsErrors + latest-all analyzers (Roslynator, Sonar, VSTHRD)
dotnet test RustPlusApi.sln # runs every test project on BOTH TFM hosts (net8.0 + net10.0)
# Single test class/method (still both TFMs):
dotnet test RustPlusApi.sln --filter "FullyQualifiedName~ClassName.MethodName"
# One TFM only (net8.0 host = exercises the netstandard2.0 build):
dotnet test RustPlusApi.sln -f net8.0
dotnet test RustPlusApi.sln -f net10.0
# Coverage with per-class gap report + CI gate (line 95 / branch 90):
tools/coverage/report.sh
# Formatting + member reordering (ReSharper CLI; whitespace rules from .editorconfig, the
# ReformatAndReorder cleanup profile + member layout live in RustPlusApi.sln.DotSettings):
dotnet tool restore
dotnet jb cleanupcode RustPlusApi.sln --profile="ReformatAndReorder"
# Mutation testing (run from the unit-test project directory):
cd tests/RustPlusApi.Fcm.UnitTests && dotnet stryker --config-file stryker-config.json --project RustPlusApi.Fcm.csproj
# Docs site (DocFX, needs `dotnet tool install --global docfx`):
docfx docs/docfx.json --serveA committed pre-push hook (.githooks/pre-push, wired up automatically by the first
dotnet build) runs the ReSharper formatter and member reordering on outgoing files and
rejects the push if it changes anything — format before pushing.
RustPlusApi(core) — WebSocket client for a Rust server's app port. Two layers:RustPlusSocket(abstract: connect/dispose lifecycle, channel-based send loop, receive loop, request/response correlation byseq, rawAppRequest/AppMessageevents) andRustPluson top (typedResponse<T>methods likeGetInfoAsync, plus broadcast-driven events likeOnTeamChatReceived).Data/holds the public DTOs;Extensions/holds the protobuf→DTO mappers.RustPlusApi.Camera—CameraController(subscribe/keep-alive/input over aRustPlusinstance, capability-gated movement) andCameraRenderer(frame → image via ImageSharp).RustPlusApi.Fcm— FCM push listener:RustPlusFcmSocketspeaks Google's MCS protocol over TLS tomtalk.google.com:5228;RustPlusFcmdecrypts notifications and raises pairing/ alarm events.RustPlusApi.Fcm.Registration— native credential acquisition replacing the rustplus.js CLI: GCM check-in → Firebase/FCM/Expo registration (Steps/), Steam login via Chrome DevTools Protocol (SteamLoginService), Rust Companion registration,CredentialsStorepersistence.- Two
*.Extensions.DependencyInjectionpackages —AddRustPlus/AddRustPlusFcm+ factories.
Core contracts are generated at compile time by protobuf-net.BuildTools from
src/RustPlusApi/Protobuf/RustPlusContracts.proto. That proto is authoritative — it is
regenerated by decompiling the actual Rust dedicated server via tools/update-proto/update-proto.sh
(see its README; the ProtoRefresh.yml workflow runs the diff gate). RustPlusApi.Fcm and
.Fcm.Registration instead use hand-written code-first protobuf-net classes (ProtoBuf/Mcs.cs,
Protobuf/CheckinContracts.cs).
- Test projects target
net8.0;net10.0: the net8.0 host can't load a net10.0 library asset, so it resolves the netstandard2.0 build — the same xUnit suite validates both compiled outputs ("multi-TFM parity"). Behavioral#ifforks must be pinned to agree by tests. tests/RustPlusApi.MockServeris an in-process WebSocket server shared by integration tests.- Live-network paths (TLS connect, Steam login, MCS handshake) are
[ExcludeFromCodeCoverage]with per-member justifications; the pipelines they feed are tested throughinternalseams (InternalsVisibleTo, e.g.RunReceiveLoopOverStream,*ForTestsmembers). Everything else is expected at 100/100 line/branch — additions to the exclusion list need a justification indocs/development/testing.md. - Stryker cannot mutate the core
RustPlusApi.csproj(protobuf-net.BuildTools breaks its rollback compiler); core behavior is pinned by exact-assertion unit tests instead.
Local builds always produce version 1.0.0; real versions are injected by CD
(-p:Version=<tag> on release). Don't bump versions in project files.