Update OpenWiki documentation and claims
Some checks failed
Build / build (pull_request) Has been cancelled
Some checks failed
Build / build (pull_request) Has been cancelled
Reference AGENTS.md in CLAUDE.md and record verification claims for the architecture overview and operations runbook. Signed-off-by: Tim Krampitz <Tim.Krampitz@live.com>
This commit is contained in:
@@ -2,11 +2,6 @@
|
||||
|
||||
## OpenWiki
|
||||
|
||||
This repository has a generated `openwiki/` evidence index. It is optional just-in-time context, not required startup reading.
|
||||
|
||||
- Treat source code and tests as authoritative. A brief's unknowns and review items are verification gaps, not automatic requirements.
|
||||
- Prefer the narrowest quiet validation that proves the changed behavior. Preserve complete failure output.
|
||||
|
||||
The scheduled OpenWiki GitHub Actions workflow refreshes the repository wiki. Do not hand-edit generated OpenWiki pages unless explicitly asked; prefer updating source code/docs and letting OpenWiki regenerate.
|
||||
See [AGENTS.md](AGENTS.md) for OpenWiki agent instructions.
|
||||
|
||||
<!-- OPENWIKI:END -->
|
||||
|
||||
150
openwiki/.claims/architecture/overview.json
Normal file
150
openwiki/.claims/architecture/overview.json
Normal file
@@ -0,0 +1,150 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"pageVersion": "sha256:9c2c9eb1e1f116019b34b3aa1db0c6851e923a41cfa25a3c8c135be366ff8c37",
|
||||
"claims": [
|
||||
{
|
||||
"id": "claim_f26387ba0aa64fc7b0d39814b6ef681d",
|
||||
"statement": "The .NET Aspire AppHost in SlipItIn.AppHost/AppHost.cs provisions a PostgreSQL container named 'pgsql' with database 'postgresdb', registers the backend SlipItIn.Server project with a reference to and WaitFor dependency on the database, and registers the SlipItIn client project with a service-discovery reference to the server.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.AppHost/AppHost.cs#L1-L13",
|
||||
"version": "repo-lines-v1:sha256:5b10af1a2cea5378effced1c0adef3ecd6049d5f05a4ed540cb701ac1cf37d9c:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjA3OTVkNWZmODQyMTVmOTA0YTA4ZjU5MmI2MGNkZmM4MTBiZjk2YThhYzJhMjYzZDJlMzZhMDJmZjZhMWVlYjEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU0M2YyYWU1YzNkZTAwZWQyY2NiNjBiNjBkM2I0Yzk0NTc5ZTVmMDg1MjMwNDRjOTRhOTcyODQ1OWZiZDFlZjciLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_d203ca24ede64d428fe33626b57b252c",
|
||||
"statement": "Program.cs in SlipItIn.Server registers IDbContextFactory for thread-safe EF Core access, configures JWT Bearer authentication with OnMessageReceived for SignalR query-string extraction and OnTokenValidated for user-existence checks, adds SignalR hubs, CORS AllowAll policy, controllers, and OpenAPI, and at startup runs Database.Migrate and DbSeeder.SeedAsync.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Program.cs#L1-L143",
|
||||
"version": "repo-lines-v1:sha256:42ad9155f6cc7dda925cb2f2de4409e19d7051ea97416bfcfc0959b2c4fbef3d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQzLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiI1Y2FjYmU0OTcyM2JiNzJmZTQwODdlMDVlNWMxYmI1NDk5MTQ0OGRiOTY5NDJmYmVkYWY4Y2QwZmNkNGExZDdkIiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiIyYzMyYmVkMTM2YTNlZGMwNjM3MWU3MTkwNjJjYjMyNDE1NmE0MWVlZDdlYTc2ZTIwZWQ3ZmZjOTkyNzViODQxIiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MCwicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MCwiZm9sbG93aW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1In0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_645e376f85d9494792453dc50a93c5c8",
|
||||
"statement": "JWT authentication for SignalR is secured by OnMessageReceived extracting the access_token query parameter only for paths starting with /hubs, and OnTokenValidated creating a short-lived DbContext via IDbContextFactory to verify the user still exists and is active before any hub method executes.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Program.cs#L57-L88",
|
||||
"version": "repo-lines-v1:sha256:a53b47cadec43a5a8d8dd2af6d6163507c8ba8b4492682545da739babe1fe4c4:eyJzZWxlY3RlZExpbmVDb3VudCI6MzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijc3YTY5ZDgzMDYzMGI1N2IxZGQwMWM2OTRhOTczN2IzOGRhNjUxZDdiYjkwYWYxZTY5YmM5ZmFhZjYxYjlmODgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjkxMDU5YzhjZmI1YTA3MmM2ZjY0ZGQzZDFiZmRiYzM1NmZiZWM5M2FlZWM4ODc5MWRhN2VkYmE4OWMwODg1ZGQiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImU3M2E0NGY1YTM0M2QyNGI5NWU3NjMyMWZiOWI4ZmZhNWMzMzVhY2I1MDcxMTVhOGMzOWIxNTI0NDcxNzBiM2EiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjlhZGRjNGEzN2JiZDE4ZjU0YjNmNjcxNGUyNGNkNjNkZGE0OWVmNjEzMjQ0ODVmMDhjZDZmNDc1ZjMzODViODUifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_ce6c77ab5aea413d8c946333b75d0ad5",
|
||||
"statement": "GameHub provides five authorization methods: GetAuthenticatedUserId extracts the NameIdentifier claim; ValidatePlayerAccessAsync verifies Player.UserId matches the caller; ValidateHostAccessAsync verifies Game.HostId matches the caller; ValidateUserInGameAsync verifies the caller is a game participant; ValidateChallengeTargetAccessAsync ensures only the accused player can resolve a challenge.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Hubs/GameHub.cs#L29-L100",
|
||||
"version": "repo-lines-v1:sha256:b6baa264558236461ef7bf26d6614878e5b3229aa09d0f4de431514f973c8039:eyJzZWxlY3RlZExpbmVDb3VudCI6NzIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImE4MWI3MTE4ZDQwNjdlMjA2N2I2NmUyZmNjNTczMzNiNzg2YTM3NTc4NGYxNjQ0OTk0ZGFlYzBlNWQyZmNhODAiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE0MTlhNmRjYzA0ZTIxM2EzYzdhNzdhNDU2YjAxZTc2ZDFlZjc4Y2QwMGJiZWMxZDhmODE0NThmZTkzZWRjNTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjljMTQ2ZTY0YjVlZjlmNGFkNjQzMTFlNTgxNzdiOTYwMGJlZmJkMjhlZWEzN2MzNDY0NTAzZThhNWYyZmU4ODYiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImJjNGQwYjMxNjQ4N2E2NGQ2MWUxY2NlZDYxOTVkMDQyOTgwMjc4MGFiN2I3ZTBmNDI5YTY4NGExM2ZhNmIzZGMifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_8db66fb0b33c4708a62bdf96f81400fe",
|
||||
"statement": "GameHub.OnConnectedAsync detects if the reconnecting user is already in a Lobby or InProgress game, stores the new ConnectionId, and re-joins the SignalR lobby group; OnDisconnectedAsync clears ConnectionId for all affected players to prevent messages to dead connections.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Hubs/GameHub.cs#L365-L427",
|
||||
"version": "repo-lines-v1:sha256:b1f8a01a846a8dcea0e72cc0a48bb42d85da5a7a22a221566c516c7d133a461d:eyJzZWxlY3RlZExpbmVDb3VudCI6NjMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImZhYWJhMjJiODNiMTRlYTBlNzAwZWZiZGJlODcyYmU3YjUyOTVhMmMzMTMyMGJiYjdmMWJlMzkxYWY1ZjZhZDUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUyMmZhYTUzNWJkMmVhM2Q1MmU3NDFlZDQ1NDQzNWI3ZmU0OWU4MzU4NmU5NDNkOThkODJiYmZiMjRkMWY1YzUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM3NTRlYmJmZDhlZmVhNGQ0OWVjNWM3YTM4Yjg3MTYwNWNlNGYzMGM5NTY3NmQ2ZTU5MTBmYzQ4ZjUwMzQ1MTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_c49f2d5d78ef4025890205ea625a5b6b",
|
||||
"statement": "MauiProgram.cs registers singletons for IAppConfigurationService, ILocalStorageService, IAuthSessionService, IApiService, ISignalRService, IGameStateService, LobbyViewModel, and GameBoardViewModel, and transients for LoginViewModel and RegisterViewModel.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn/MauiProgram.cs#L22-L33",
|
||||
"version": "repo-lines-v1:sha256:0f35d7fb5dd09ff85f16adfd591b5cf3da4f938e550c52bf7d3dc9eab4b42fdd:eyJzZWxlY3RlZExpbmVDb3VudCI6MTIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQxYmU0ZGRhMGEyYTNlMGNiYjVlOGRkNGZjOTQ3MmU4MWM2MDNhZGRmYmE4MjIyOGViNDIzYTM4MDlhYTY1NDgiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlYjcwMjU3NTkzZGEwNmY2ODJhM2RkZGE1NGE5ZDI2MGQ0ZmM1MTRmNjQ1MjM3ZjVjYTc0YjA4ZjhkYTYxYTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjNiMmFjMWFiNDkwY2M4NDg4MTYyNTNjZTMyYTExYTQ0ZDhhNDUxMmFiMzlkOTcyZDQ4ZmQ2YWRiNTNkZjEyNWMiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAwMWFjMDM2ZWY0ODhkYTUyNmZmNGE2MDg0YjE2ZjVlNmMzNzZhNzM5Njg5ZDQ2YjI5YTQ4OTViZjNkYjFjNDEifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_913269edbc9543739a8a174940dde0cb",
|
||||
"statement": "GameStateService subscribes to ISignalRService C# events and translates each into a strongly-typed WeakReferenceMessenger message, including LobbyCreatedMessage, GameStateChangedMessage, GameStartedMessage, PlayerHandChangedMessage, ChallengeReceivedMessage, ErrorOccurredMessage, ConnectionStateChangedMessage, and SyncStateChangedMessage.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn/Services/GameStateService.cs#L1-L62",
|
||||
"version": "repo-lines-v1:sha256:6a100233b062c19004f61a4b92579fe0b09c3a108b51a25702463a2b1a0afeda:eyJzZWxlY3RlZExpbmVDb3VudCI6NjIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM2NWRiODRmNWEzNTgxNjY0NDk0MGJkZGNlZDZlYmM3NTljMmM2ODYyNDlmNzQ0ZmQxOWY3ZjY2ZGJhYTY5NjUiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE0MTlhNmRjYzA0ZTIxM2EzYzdhNzdhNDU2YjAxZTc2ZDFlZjc4Y2QwMGJiZWMxZDhmODE0NThmZTkzZWRjNTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImE5ZDc4OGExN2IxMjNhZTRjZTFiZThlNzJjNWIxYjRjMmI2NDUyYTQ3ODllZjgyODQyYzQxNDY0ZWRkMzE5N2MifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_e23b846d30ab452db2fc8b3d09d56584",
|
||||
"statement": "GameStateService.EnqueueActionAsync persists game actions as JSON-serialized QueuedGameAction objects in LocalStorageService when offline; on reconnect ResyncAsync calls FlushQueuedActionsCoreAsync which replays actions sequentially through SignalRService.SendQueuedActionAsync with up to 3 retries per action before dropping poisoned entries.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn/Services/GameStateService.cs#L118-L199",
|
||||
"version": "repo-lines-v1:sha256:7b0835e72e6b6bc04c75c5df62801b8e2753bd5759afa41f270ff442f7f295fa:eyJzZWxlY3RlZExpbmVDb3VudCI6ODIsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjFhMmFlZTkyZDgzMTVlZTRjNWRkYWI3OGZjNGM1MmUwODM0NmMzMTNkZDI0MWM5YTVhZjZmZWIzZmYwMWIyMjYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE0MTlhNmRjYzA0ZTIxM2EzYzdhNzdhNDU2YjAxZTc2ZDFlZjc4Y2QwMGJiZWMxZDhmODE0NThmZTkzZWRjNTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImM3NTRlYmJmZDhlZmVhNGQ0OWVjNWM3YTM4Yjg3MTYwNWNlNGYzMGM5NTY3NmQ2ZTU5MTBmYzQ4ZjUwMzQ1MTAiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImJkZjUwY2NkODM0NzE0YzI2YmI2MzU1OGQ0NjM3ODdkYjFmMjViMGIzMDU4YTBmMWZkNzI1NzA4OGU4ZTQ1MDEifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_07b56e629228493ba5b7f7cf003a13d4",
|
||||
"statement": "All database access in GameHub, GameService, AuthController, and Program.cs OnTokenValidated creates short-lived DbContext instances via IDbContextFactory.CreateDbContext or CreateDbContextAsync, preventing thread conflict exceptions under concurrent SignalR hub requests.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Controllers/AuthController.cs#L38-L38",
|
||||
"version": "repo-lines-v1:sha256:77ab5090a78dcb1145419669804c06b6366749f7806fc98d047a64608f902c41:eyJzZWxlY3RlZExpbmVDb3VudCI6MSwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNzdhYjUwOTBhNzhkY2IxMTQ1NDE5NjY5ODA0YzA2YjYzNjY3NDlmNzgwNmZjOThkMDQ3YTY0NjA4ZjkwMmM0MSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNzdhYjUwOTBhNzhkY2IxMTQ1NDE5NjY5ODA0YzA2YjYzNjY3NDlmNzgwNmZjOThkMDQ3YTY0NjA4ZjkwMmM0MSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNDU5NzA0MDgwZTNhZTE4MzVkZTJkMzEzNTk5YTA3NjY3MjVmMjdhN2JmNDQwNDgxNWI5Yjc5NGI0NjA2MjAyZiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZTU0OWU0YzFmZDIzNDY0OThjNGVjMWRlNGVlZGMwYWRiN2VjYzlhYmU1NDNjODAzNzU0OWI4N2ZlNzU3ZWM2MSJ9"
|
||||
},
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Hubs/GameHub.cs#L42-L43",
|
||||
"version": "repo-lines-v1:sha256:c70cfdcd685e60d484d7eb9539d16eb08ab941f2061620980eab2eb33a31fa3c:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNzdhYjUwOTBhNzhkY2IxMTQ1NDE5NjY5ODA0YzA2YjYzNjY3NDlmNzgwNmZjOThkMDQ3YTY0NjA4ZjkwMmM0MSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiOTQzZTZkNGQ4MDE5M2NmNDczMTM1NmZmMjFhZWVkMmIyOTBiOTNiZDUyY2UzMWY3MmE5OTkxMzZlMjVhMzQ0NSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYTU2YzQ0MWNiNWYzODUxOTc1Mzg3N2IxNjAyNGI0OWZmN2I5Njk3Nzg4MDk5NDU4ZDAwYTBmNTAyMDc5OTAzNSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiOWU4MDM4Nzg0MmVmN2QzY2E1OTRmYzkzN2Q5Mjk3OWJmMmM2YTNmMzM3ZjRkMWMxMTk4OTA2MGVjMjdkZGVmNiJ9"
|
||||
},
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Program.cs#L17-L18",
|
||||
"version": "repo-lines-v1:sha256:bff89b037a7c9439dad1c8b40eccf119306fadaa4413ad8b97b10e64b6b4addb:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiMWUyNzAyZTQ2NjJhNTBjNTZmODljZjE3Yjg1MTZmYTg0NjQ0MTY0ZjQ5N2RhMDEzMGFmZTBmOTZkNjE0NDNhNCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZGZjNDg2M2VhN2Q3MTBjZDI0MmRiNWVmYWJhYmQ2MTE5ODYwZTM0YzlkMTg0MTQ4YTQ1MzVmNDRjNGViZmJjYiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiYjhjMDY4NDZjMzI2YjQ5MzNhNGVhZTI4M2Y5N2M1NDJhNTU0MjllYzU4OTM0NzMwM2ViNGYzNDY5ZDY0MWQ3MyIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiM2QwY2U1NGY1Y2I1ZTM0YTRkNGUwMTJjODk4NGFhMTk1ZWRkYzM5OWM0MDEyMDZiN2IyYzc1NDgxYjIyMjkyYSJ9"
|
||||
},
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Services/GameService.cs#L11-L14",
|
||||
"version": "repo-lines-v1:sha256:d012048db2a9abaa071e8af375215fddb233b1568c5d8d0d0f17fea852e48a5e:eyJzZWxlY3RlZExpbmVDb3VudCI6NCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZGYwNDNlZjg2YWQ4YTI1MmYwMDAyYjYxZDY0NzEyMjdmMzMxODgwMDJkNTI2ZmMyNTljNzAxOWI3YzRhYzkyZCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMjc2N2MzY2U5MmMwY2NmOGIwYjRhOWQ1ODIxYmYxNGQ5MDJkYTM3Y2FiNDA2MDQxYWIyZWMzMzYzN2YzODJhZCIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZWE1ZDA4NzY3ZWJhNGZlYzNlYTE2NjEzMjMzMzc0ZWEyZDkwYzliNGU0ODBmNGQyZTRmMWFiNDczODAyOTNjMCIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiMjBjYWRmY2MzNTE1OTZkYzliMzMzOTJjNDY3OWU5ZTU4Y2U4OWE1YTkzMWVhYWJmMTZhNjY3MThkYmI3YmViMCJ9"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_29bd98f9e4864a119c2725b72e0c5e50",
|
||||
"statement": "GameStateDto is broadcast to all lobby players but contains no card text, only CardCount per player; PlayerHandDto containing actual phrase text is unicast via Clients.Client(connectionId) only to the owning player, ensuring opponents cannot discover card content from network traffic.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Hubs/GameHub.cs#L200-L209",
|
||||
"version": "repo-lines-v1:sha256:df5be4995be1bf304a8eb363a228f2873280db6ef56d8fdb92392f2afab1a137:eyJzZWxlY3RlZExpbmVDb3VudCI6MTAsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlYjcwMjU3NTkzZGEwNmY2ODJhM2RkZGE1NGE5ZDI2MGQ0ZmM1MTRmNjQ1MjM3ZjVjYTc0YjA4ZjhkYTYxYTYiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImM5YTg1NzJiODg4YjA4Zjc5Nzk4YTE5YWNhZDFlZmRhZmVkMDI3MDQyNWEzZTFjMjJkODg4ZDEwNmQ2NTY4M2IiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjhhNjY3MzEyOWYwMTk1NTQzZjEyMjJkNTRlOTk0OGU3MTAxYWRjMDczMGQ0YjE0MzdhMjhlZTcxZjNmZTY1ODUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImU0OTJkNGNkNjU1ZGYzYWViMGE1NjEzMzdhNjViYjIwMGM1OTIwNTgxYThlNzczNjBkYWQyOGNjZTM2MDA2NTUifQ"
|
||||
},
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Services/GameService.cs#L253-L327",
|
||||
"version": "repo-lines-v1:sha256:57d62dc5c76f75b5f6785dae111fb31c3ed5d709d982d09ed7a4ce670629c5fa:eyJzZWxlY3RlZExpbmVDb3VudCI6NzUsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImQ2YjYyMmRkODk2YTgxODdlZDU2ODY2NWExNDE3OTEyYmY0YzYyODI5NmVmMTQwZjZjZmZhN2ZhZjE5NmY5MTkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE0MTlhNmRjYzA0ZTIxM2EzYzdhNzdhNDU2YjAxZTc2ZDFlZjc4Y2QwMGJiZWMxZDhmODE0NThmZTkzZWRjNTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjdlOTViODAwZmFiMDU0OGJlYThlMmVhYzU0NmVhZjU0MTAzZmM2NWI1Yzc1NmMzYjZhYmI2YmZlYWY2ODI2NzEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjRjZWUyNzBmNTdiNjJkZDY2ZTE4NjI1NThjZDdmOWUxMTg0NWI4ODhiYzI5YmI3MWI0YTAwN2I4NGE1MjhjMGEifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_d1cae5dd74ab46b3ad3f2f737cfe6627",
|
||||
"statement": "LocalStorageService uses platform SecureStorage exclusively for the JWT auth token, and Preferences for all non-sensitive cached data including auth_user, game_state, player_hand, and queued_actions.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn/Services/LocalStorageService.cs#L1-L110",
|
||||
"version": "repo-lines-v1:sha256:b8701c760891572e530b7bd10fa6e7871c9eaa6b3941f4d0bbb438cc06926628:eyJzZWxlY3RlZExpbmVDb3VudCI6MTEwLCJmaXJzdFNlbGVjdGVkTGluZUhhc2giOiJjNjVkYjg0ZjVhMzU4MTY2NDQ5NDBiZGRjZWQ2ZWJjNzU5YzJjNjg2MjQ5Zjc0NGZkMTlmN2Y2NmRiYWE2OTY1IiwibGFzdFNlbGVjdGVkTGluZUhhc2giOiJlMjJmYWE1MzViZDJlYTNkNTJlNzQxZWQ0NTQ0MzViN2ZlNDllODM1ODZlOTQzZDk4ZDgyYmJmYjI0ZDFmNWM1IiwicHJlY2VkaW5nQ29udGV4dExpbmVDb3VudCI6MCwicHJlY2VkaW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1IiwiZm9sbG93aW5nQ29udGV4dExpbmVDb3VudCI6MCwiZm9sbG93aW5nQ29udGV4dEhhc2giOiJlM2IwYzQ0Mjk4ZmMxYzE0OWFmYmY0Yzg5OTZmYjkyNDI3YWU0MWU0NjQ5YjkzNGNhNDk1OTkxYjc4NTJiODU1In0"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_c3a287d4f636401f9913e4e013d41fbf",
|
||||
"statement": "ServiceHelper captures MauiApp.Services at startup and exposes a static GetRequiredService method for resolving dependencies in contexts where constructor DI is unavailable, such as WeakReferenceMessenger handlers.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn/Infrastructure/ServiceHelper.cs#L1-L16",
|
||||
"version": "repo-lines-v1:sha256:6fae980d1a7b7a70ae8f3d6cbb917465716b173b1993cfd0095859f34360ca02:eyJzZWxlY3RlZExpbmVDb3VudCI6MTYsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImU5NzdlOWYyOTk3MjdiZjE4YjRjNDc3NDY4ZTg0OTdhNzgzYTkxODI0OTNhZDgyOWJkOTlmNzU0ZTAyZGM2MWQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUyMmZhYTUzNWJkMmVhM2Q1MmU3NDFlZDQ1NDQzNWI3ZmU0OWU4MzU4NmU5NDNkOThkODJiYmZiMjRkMWY1YzUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ"
|
||||
},
|
||||
{
|
||||
"resource": "repo://SlipItIn/MauiProgram.cs#L38-L39",
|
||||
"version": "repo-lines-v1:sha256:6aa9ec7803623036281bba0c2de1bc4c936f6394f0decaf9ca0b3d8fbfbab0d5:eyJzZWxlY3RlZExpbmVDb3VudCI6MiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiZGE0NGQxZjIzNzQ1MzE2NWYxNThjNmVkZmU5MWY3Y2Y0NWM0NDcyY2UwNjkwY2QwMzdjZjMxYTQ5MGVjMzIyNyIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiNjFjMzJhN2JlNzEzOTRhMjY4ZDFhN2ZmZGRmOWZhZTBmODc1YjE3MzUxOWQxMjMxMDAxZDc5MmFmYjMyMThhYSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMDZlYTc3NmZlMTZhYWUzY2M0M2YyNzdkZTA3MmYwOThlYTkxYWVmMjgxY2E3Yjg5M2Y0N2M1MGI0MzFhOWYzMiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNmExMWFjZGY0MjE2ZjgzZDQ2MTQwZGJkOTk0MzIyMjczOWE0MTFmZWRiNDgyNTg4Njc5ZDhlZGQ3NDk3ZTNjMSJ9"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"verification": {
|
||||
"by": "openwiki/0.5.0",
|
||||
"at": "2026-09-07T15:04:46.288Z"
|
||||
}
|
||||
}
|
||||
82
openwiki/.claims/operations/runbook.json
Normal file
82
openwiki/.claims/operations/runbook.json
Normal file
@@ -0,0 +1,82 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"pageVersion": "sha256:5389d38dcacf5b524555f17ca858a9e10106a078dd4c0c728b661386db1a9df2",
|
||||
"claims": [
|
||||
{
|
||||
"id": "claim_26d0b751207c4d5b8e6d388c90eaf669",
|
||||
"statement": "The SlipItIn.Server.Tests project uses xUnit as the test framework, Moq for mocking dependencies, and EF Core InMemory for isolated database contexts targeting net10.0.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj#L1-L24",
|
||||
"version": "repo-lines-v1:sha256:629d0dd4e27bacd5339d63ae351decad7b2b34d6e363d4c2c054d01c2ecb80e1:eyJzZWxlY3RlZExpbmVDb3VudCI6MjQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijg4YWZmMzdmYzg4YWFiODM0YjQ2ZDQ3MTk2NmM0MmQ5NGM5Y2U0MzhhNWM0YzkwYjMyOGU2ZDI0Mzg3OWI3MGEiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjdlYjcwMjU3NTkzZGEwNmY2ODJhM2RkZGE1NGE5ZDI2MGQ0ZmM1MTRmNjQ1MjM3ZjVjYTc0YjA4ZjhkYTYxYTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjczM2NlYjI1ZTIyMDgzNzgyMzdhYTNlMmY2ZmNlZTM3ZTE1MThlMzk1MGExZTJlYzgyN2QwNjgwYzI2NDVhOTMifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_1ce4b53277a7491cb6db61bc1b4f7b40",
|
||||
"statement": "TestDbHelper.CreateFactory() creates a unique InMemory database per test via Guid.NewGuid(), ensuring complete test isolation for all test classes.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server.Tests/TestDbHelper.cs#L12-L18",
|
||||
"version": "repo-lines-v1:sha256:fb006bf0c6e5859dac64f65fafbb8343db47c78c986bbdf146c32db7044251b6:eyJzZWxlY3RlZExpbmVDb3VudCI6NywiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiNWQ1NGE1YWM1YTYxN2Q0NjE0NDg0YWViMzgxMTUyZjc2M2EzNDkwMDg5M2QwNzAzMzYwYTc5ZDk2NGUzNjg2MCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiMTQxOWE2ZGNjMDRlMjEzYTNjN2E3N2E0NTZiMDFlNzZkMWVmNzhjZDAwYmJlYzFkOGY4MTQ1OGZlOTNlZGM1NiIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiMjU0MTc3ZTM4MTFiMWE2OWIwYmM5MDVjZDI5Mjk2ZDRjZjdiMTcyMGEzMWQwODY4NTcwNDk4OTZmNjMzYzI5MiIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiY2JhNjY0N2QwMGVjNWI0ODBlNjYyYjdiMTY4ZTlhYzY5NDc2ZTg5MDdkODEzMGM2MWQ5ZDI3ODlmNjVmMDBmOSJ9"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_e739f028ae944fdda6bdbece1cb45b51",
|
||||
"statement": "Jwt:Key has no default in production configuration; in non-development environments a missing or short (less than 32 char) key causes a startup InvalidOperationException.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Program.cs#L24-L37",
|
||||
"version": "repo-lines-v1:sha256:d25ada09392171a160b04ea576cca0ebd042681d55ffa5fad372c750fcd9e75d:eyJzZWxlY3RlZExpbmVDb3VudCI6MTQsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjU1MzZiNWQ3ODQyYjVhYjgwZTJjMGIyN2JhYjMzZjlmNDE3MmE2MmIyMTQ2MmRkZDA2NDZhOGM1MGZjODBmMTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImUyMmZhYTUzNWJkMmVhM2Q1MmU3NDFlZDQ1NDQzNWI3ZmU0OWU4MzU4NmU5NDNkOThkODJiYmZiMjRkMWY1YzUiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImRkYWRlZGRjZDdjMzA5MGFmMDI0MWM1ZGFiYTExN2E0Nzk4ZTU1OGU4YjQwNjQ5ZjU2MmQ1YzQzYWM1MTQxZTQiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjJmMDU1YzhjZjNkMjI5MzkxODY2NGU5NDVlNDIyODRhNjhmZjQyMDBiNGEwMDYyYTQ5YWIxM2NhYTgxMjQyNTMifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_c13a8df4af4345b68e40ab4ba95d109f",
|
||||
"statement": "The CI workflow builds the server stack projects with Release configuration and the MAUI client with Debug configuration, then runs server tests in Release.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://.github/workflows/build.yml#L28-L40",
|
||||
"version": "repo-lines-v1:sha256:8bb502095ab57d36f4be6522f6941fbf4284b199210de17c3f473164d3316d7c:eyJzZWxlY3RlZExpbmVDb3VudCI6MTMsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjQ4M2FhN2YyNzY2MmUwODljZjU0YjBjYWVhOTQ3ZGFjNzI0MWJhOWRiYzc0MTRhZTZmY2ZhMWViMjhkNTJhNTMiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6ImY1MWI2NmM2MDcwMWY0YTUxMWE1YmFlZDY2ZWFmMWFjYzJhN2EwZmM5NDQ2MDE1MDMxN2U1ZGM3N2E3MDg1ZGIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjozLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6IjljMmFjMDNkNmRiZmJkMTQ0ZTg5YTViNTM5YjVhYmJlZmVjZjZhMWQxODg3OWFiMGJlMzYwOGM4M2Q1MGYxYjEiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjowLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUifQ"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_f68c2ed07f3b4f9f9c899291de56124a",
|
||||
"statement": "On Aspire launch, Program.cs automatically applies EF Core migrations and calls DbSeeder.SeedAsync to populate initial users and phrases when the database is empty.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server/Program.cs#L116-L121",
|
||||
"version": "repo-lines-v1:sha256:16925ea3cd8dbeb515e18474d5172957806fa02bef1d4070331d55bc6f3f45ce:eyJzZWxlY3RlZExpbmVDb3VudCI6NiwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiOTNmYjQ4ZDcxMGRmZGEyMDFjNTM0ODg3MDEyYWQ5NjYxYjBjN2RjZTU0ODdjYTFlMmRhMGNlYzFmMmU3Yzg2NSIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZTIyZmFhNTM1YmQyZWEzZDUyZTc0MWVkNDU0NDM1YjdmZTQ5ZTgzNTg2ZTk0M2Q5OGQ4MmJiZmIyNGQxZjVjNSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjMsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiNTk1YmY3MTIwNTM3ZmFiOGI0YWVjZWUxNDRlODQwZWVjNDJiYWJkNjJkM2YwYzJmZDMxYWFlM2Q0MDc3MTEyNSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiNWM3MjJkZTIyM2VkMDYwNzU1NDRmMmNmZjVkYzM5OGRjMjJjNjY2ODhiOGY5MzhhYzU4ZDFjMjljNGI4YjExZCJ9"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "claim_684e6b8a21e2409990461d5ad54a109e",
|
||||
"statement": "GameServiceTests covers business logic (create/join/start game, deal cards, submit slip, challenge resolution, game state), GameHubTests covers SignalR hub behavior with mocked IGameService, AuthControllerTests covers registration/login/me endpoints, and DbSeederTests covers seed idempotency.",
|
||||
"evidence": [
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server.Tests/AuthControllerTests.cs#L1-L17",
|
||||
"version": "repo-lines-v1:sha256:ae5410c6ed58240961e547295c52a166d4f92097e058fd55d7bd5662319f15a0:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6ImI3NWM5ZTU2YzE0ZTgwZmY3ZWZlODNmNDQzZmQ4YzY4MGIwNzgxYTNmNWQ2MmU1N2JiOWI3MWRjMGU1YTIxYzkiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6Ijk2YjMxY2JmNGUzYTI3MTZhMDAyMWJiNDU1OTdmOWMxY2IwYjMwYjRlM2QzZDc1NzMwZjIyMTc0Y2VmNmI4MmMiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6IjAyYTQyZmUzNmM1MTY3YzIzNTAxOThmNmZkZjYwOGUwNjJhYTgyODE3NzI5ZDFkZDRjMzBhYzlmM2NhNGVjYjUifQ"
|
||||
},
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server.Tests/DbSeederTests.cs#L1-L8",
|
||||
"version": "repo-lines-v1:sha256:78405718fae114f2d1ac469e76a7f7e766fe5fe477461d5709fe20919317edb2:eyJzZWxlY3RlZExpbmVDb3VudCI6OCwiZmlyc3RTZWxlY3RlZExpbmVIYXNoIjoiODI1ZWYzYjZhMGMwNTM4MTAwNzMyNjZmMjJmNDE0ODlkOTc5NDE2ODk3NjI4MjA2MjFmYThlNzRjMmYwMTlkNCIsImxhc3RTZWxlY3RlZExpbmVIYXNoIjoiZmE2ZWZmMDIwMTUyMzgxY2RlZmY1NzEzYTFmZmYwNWRjZTUxN2NjMmFiNTM3YTcxZjUwYmY5NTNiMjVkYWVkMSIsInByZWNlZGluZ0NvbnRleHRMaW5lQ291bnQiOjAsInByZWNlZGluZ0NvbnRleHRIYXNoIjoiZTNiMGM0NDI5OGZjMWMxNDlhZmJmNGM4OTk2ZmI5MjQyN2FlNDFlNDY0OWI5MzRjYTQ5NTk5MWI3ODUyYjg1NSIsImZvbGxvd2luZ0NvbnRleHRMaW5lQ291bnQiOjMsImZvbGxvd2luZ0NvbnRleHRIYXNoIjoiZGVjM2IzMDRiZjIxYTdiYmRkZjE3ZWRkZGEyNDcwNWVjMzA5OTFlZDY1ZjcxY2ZiNGI3OTQ2NWQwMWNkZGI0MyJ9"
|
||||
},
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server.Tests/GameHubTests.cs#L1-L18",
|
||||
"version": "repo-lines-v1:sha256:2aebd88a387ba57ca7c688410d187ac0e9090f706e85a0d601957c0943b21c9f:eyJzZWxlY3RlZExpbmVDb3VudCI6MTgsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjVjOWEzNmMzYzcxMmRlN2ZlOTBlOTEwM2QwZTUyNmExOGE4ZjkwMThjMTY2ODk5ZGQxOTdmNmU4OGZhMzRmYjIiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjczN2NlMmFiZTQ2YTYxNjU0NDFmMDcyM2IyN2NmOGU0ZTI0NDFiNGI2YTE0NTliZjk1MjI5ODFhZWVkM2NmYmIiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImQwNzAzZWMyMTNiZTBmZGEzYTJhZjVhOTExMTkxYTlkM2Q5ZDQ4YmVlOTNlNTdkMTFiNDI2Njg1MzdjYWQ4MTIifQ"
|
||||
},
|
||||
{
|
||||
"resource": "repo://SlipItIn.Server.Tests/GameServiceTests.cs#L1-L17",
|
||||
"version": "repo-lines-v1:sha256:b692d4f82ed745136c692ff69ff4eb7ddc5fdea9c4b3b5769d1eed52c76f685f:eyJzZWxlY3RlZExpbmVDb3VudCI6MTcsImZpcnN0U2VsZWN0ZWRMaW5lSGFzaCI6IjIwYWZmMTQ0YmQwNjM0M2U0MGM5MTMwMTM0YzU0YzYzNmRiMTcwYzBiZGQ3YzZjOTNhYjcwYTBjYjY1N2YwZDQiLCJsYXN0U2VsZWN0ZWRMaW5lSGFzaCI6IjE0MTlhNmRjYzA0ZTIxM2EzYzdhNzdhNDU2YjAxZTc2ZDFlZjc4Y2QwMGJiZWMxZDhmODE0NThmZTkzZWRjNTYiLCJwcmVjZWRpbmdDb250ZXh0TGluZUNvdW50IjowLCJwcmVjZWRpbmdDb250ZXh0SGFzaCI6ImUzYjBjNDQyOThmYzFjMTQ5YWZiZjRjODk5NmZiOTI0MjdhZTQxZTQ2NDliOTM0Y2E0OTU5OTFiNzg1MmI4NTUiLCJmb2xsb3dpbmdDb250ZXh0TGluZUNvdW50IjozLCJmb2xsb3dpbmdDb250ZXh0SGFzaCI6ImJiNjQyNDNhZDEzODFmZjU1N2QwM2Q4MDU2MDVhNTI2ODRjYmZiZmJjMTBiYzMxNDdkOTg1YjBkZThmY2Q3NDQifQ"
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"verification": {
|
||||
"by": "openwiki/0.5.0",
|
||||
"at": "2026-09-07T15:04:46.288Z"
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
{
|
||||
"updatedAt": "2026-08-08T18:09:49.042Z",
|
||||
"updatedAt": "2026-09-07T15:49:53.659Z",
|
||||
"command": "update",
|
||||
"gitHead": "37616a93166c2ac0a46eedab276164f811043f4e",
|
||||
"model": "gemini-3.6-flash",
|
||||
"status": "complete",
|
||||
"model": "xiaomi/mimo-v2.5",
|
||||
"status": "interrupted",
|
||||
"language": "en"
|
||||
}
|
||||
|
||||
19
openwiki/.page-manifest.json
Normal file
19
openwiki/.page-manifest.json
Normal file
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"pages": {
|
||||
"/openwiki/architecture/overview.md": {
|
||||
"gitHead": "118660a207d4b2ec7cb3285db11ccde81ce13300",
|
||||
"sourceFingerprint": "sha256:a935b1e20566bc059db689b6f75ad2c2db50d64a9e5559dfb7ad6b9e4cb6ae40",
|
||||
"pageVersion": "sha256:9c2c9eb1e1f116019b34b3aa1db0c6851e923a41cfa25a3c8c135be366ff8c37",
|
||||
"completedBy": "openwiki/0.5.0",
|
||||
"completedRunId": "de56dc0b-8ceb-45d0-9c4f-113907ab922b"
|
||||
},
|
||||
"/openwiki/operations/runbook.md": {
|
||||
"pageVersion": "sha256:5389d38dcacf5b524555f17ca858a9e10106a078dd4c0c728b661386db1a9df2",
|
||||
"completedBy": "openwiki/0.5.0",
|
||||
"completedRunId": "de56dc0b-8ceb-45d0-9c4f-113907ab922b",
|
||||
"gitHead": "118660a207d4b2ec7cb3285db11ccde81ce13300",
|
||||
"sourceFingerprint": "sha256:a935b1e20566bc059db689b6f75ad2c2db50d64a9e5559dfb7ad6b9e4cb6ae40"
|
||||
}
|
||||
}
|
||||
}
|
||||
310
openwiki/.run.json
Normal file
310
openwiki/.run.json
Normal file
@@ -0,0 +1,310 @@
|
||||
{
|
||||
"schemaVersion": 1,
|
||||
"runId": "de56dc0b-8ceb-45d0-9c4f-113907ab922b",
|
||||
"mode": "update",
|
||||
"phase": "generating",
|
||||
"startedAt": "2026-09-07T15:04:46.288Z",
|
||||
"language": "en",
|
||||
"languageChanged": false,
|
||||
"requiredRewritePages": [],
|
||||
"initialPages": [
|
||||
"/openwiki/architecture/overview.md",
|
||||
"/openwiki/domain/game-mechanics.md",
|
||||
"/openwiki/operations/runbook.md",
|
||||
"/openwiki/quickstart.md",
|
||||
"/openwiki/source-map.md",
|
||||
"/openwiki/workflows/slip-and-challenge.md"
|
||||
],
|
||||
"sourceFingerprint": "sha256:a935b1e20566bc059db689b6f75ad2c2db50d64a9e5559dfb7ad6b9e4cb6ae40",
|
||||
"targetGitHead": "118660a207d4b2ec7cb3285db11ccde81ce13300",
|
||||
"actor": {
|
||||
"producerActor": "openwiki/0.5.0",
|
||||
"metadataModel": "xiaomi/mimo-v2.5"
|
||||
},
|
||||
"previousLastUpdate": {
|
||||
"updatedAt": "2026-08-08T18:09:49.042Z",
|
||||
"command": "update",
|
||||
"gitHead": "37616a93166c2ac0a46eedab276164f811043f4e",
|
||||
"model": "gemini-3.6-flash",
|
||||
"status": "complete",
|
||||
"language": "en"
|
||||
},
|
||||
"baseGitHead": "37616a93166c2ac0a46eedab276164f811043f4e",
|
||||
"wikiGoal": "A code wiki for this local repository. Prioritize a concise quickstart, architecture overview, source map, key workflows, domain concepts, operations/runbook notes, testing guidance, and integration points. Inspect git history to understand reasoning behind code changes and the progression of the repository. Keep pages grounded in the repository structure and recent code changes. Prefer practical navigation for engineers over generic summaries.",
|
||||
"beforeContentSnapshot": "2816cb59e100a0ec521ecb1654aa824838df3f71c939ba722c180d3c6e6816aa",
|
||||
"preparedWiki": {
|
||||
"generatedProvenance": [
|
||||
{
|
||||
"page": "/openwiki/architecture/overview.md",
|
||||
"bodyHash": "d4fe279bc8701182ae7f9c4e9ee9a639d2575470536d7125e4f3af5dae4732e4"
|
||||
},
|
||||
{
|
||||
"page": "/openwiki/domain/game-mechanics.md",
|
||||
"bodyHash": "714719e965a255b23a8507dfc96f4f0e8d189e00f6071eedc2e026e685d0885a"
|
||||
},
|
||||
{
|
||||
"page": "/openwiki/operations/runbook.md",
|
||||
"bodyHash": "13259c850ae33d991dd5f4ee5ea517ef7478a26c87b2b6f0a329120a58a25e88"
|
||||
},
|
||||
{
|
||||
"page": "/openwiki/quickstart.md",
|
||||
"bodyHash": "f868fb2b49669edd9d9ed13e97fec306890de80ae3bf45ae882b58dc0b7fec55"
|
||||
},
|
||||
{
|
||||
"page": "/openwiki/source-map.md",
|
||||
"bodyHash": "8e12954091edb0f7b92dea83e7f791903460f025a1f120f5f0f1a9af06bc2ab3"
|
||||
},
|
||||
{
|
||||
"page": "/openwiki/workflows/slip-and-challenge.md",
|
||||
"bodyHash": "b6ec60e2780bbecbf62a22b971decd6e9a0fb5aad1bb237197046e49a88b43a9"
|
||||
}
|
||||
]
|
||||
},
|
||||
"plan": {
|
||||
"pages": [
|
||||
{
|
||||
"id": "31976868-43dd-44ce-87ce-70d460077c8a",
|
||||
"path": "/openwiki/architecture/overview.md",
|
||||
"title": "System Architecture & Security Overview",
|
||||
"purpose": "Detailed technical architecture covering .NET Aspire orchestration, ASP.NET Core SignalR hub, .NET MAUI MVVM client layer, WeakReferenceMessenger event routing, dual-layer local storage, concurrency safety via IDbContextFactory, and data privacy isolation model.",
|
||||
"seedPaths": [
|
||||
"SlipItIn.AppHost/AppHost.cs",
|
||||
"SlipItIn.Server/Data/SlipItInDbContext.cs",
|
||||
"SlipItIn.Server/Hubs/GameHub.cs",
|
||||
"SlipItIn.Server/Program.cs",
|
||||
"SlipItIn.Server/Services/GameService.cs",
|
||||
"SlipItIn.ServiceDefaults/Extensions.cs",
|
||||
"SlipItIn/MauiProgram.cs",
|
||||
"SlipItIn/Services/GameStateService.cs",
|
||||
"SlipItIn/Services/Interfaces/IGameStateService.cs",
|
||||
"SlipItIn/Services/Interfaces/ISignalRService.cs",
|
||||
"SlipItIn/Services/LocalStorageService.cs",
|
||||
"SlipItIn/Services/SignalRService.cs"
|
||||
],
|
||||
"relatedPages": [
|
||||
"/openwiki/domain/game-mechanics.md",
|
||||
"/openwiki/operations/runbook.md",
|
||||
"/openwiki/quickstart.md",
|
||||
"/openwiki/source-map.md",
|
||||
"/openwiki/workflows/slip-and-challenge.md"
|
||||
],
|
||||
"instructions": [
|
||||
"Ensure all Mermaid diagrams follow mermaid-diagrams skill rules: no semicolons or pipes in labels, no unescaped angle brackets, short labels, captions below each diagram.",
|
||||
"Keep the Concurrency Safety: IDbContextFactory section — it explains the thread-safe pattern used in GameHub, AuthController, and GameService.",
|
||||
"Keep the Data Privacy Isolation Model section — it explains GameStateDto (public, no card text) vs PlayerHandDto (private, unicast).",
|
||||
"Keep the Guidance section with extension points and invariants.",
|
||||
"Keep the frontmatter type: Architecture and openwiki metadata.",
|
||||
"Keep the mermaid graph TD diagram for MAUI Client Architecture — it accurately shows Views, ViewModels, Messaging Layer, and Service Layer.",
|
||||
"Keep the mermaid sequence diagram for Real-Time Auth Flow — it accurately shows the auth and SignalR connection flow.",
|
||||
"Language: English.",
|
||||
"Verify the .NET Aspire Orchestration Model section matches AppHost.cs: AddPostgres('pgsql').AddDatabase('postgresdb'), AddProject<Projects.SlipItIn_Server>('server'), AddProject<Projects.SlipItIn>('slipitin').",
|
||||
"Verify the ASP.NET Core Backend Architecture section matches Program.cs: AddNpgsqlDataSource, AddDbContextFactory, JWT authentication with OnMessageReceived for SignalR query parameter, AddSignalR, CORS AllowAll, MapHub<GameHub>('/hubs/game'), db.Database.Migrate() and DbSeeder.SeedAsync().",
|
||||
"Verify the JWT Authentication & SignalR Security section matches Program.cs: JWT key >= 32 chars, issuer/audience defaults, OnTokenValidated checking user existence and active status.",
|
||||
"Verify the MAUI Client Architecture section matches MauiProgram.cs: Singletons (IAppConfigurationService, ILocalStorageService, IAuthSessionService, IApiService, ISignalRService, IGameStateService, LobbyViewModel, GameBoardViewModel), Transients (LoginViewModel, RegisterViewModel)."
|
||||
],
|
||||
"status": "complete",
|
||||
"completedBy": "openwiki/0.5.0"
|
||||
},
|
||||
{
|
||||
"id": "c114a18b-05fe-4a65-a9aa-80e960ec56f3",
|
||||
"path": "/openwiki/domain/game-mechanics.md",
|
||||
"title": "Game Domain & State Models",
|
||||
"purpose": "Detailed domain model covering all entities (User, Game, Player, Phrase, GameRound, PlayerCard, SlipChallenge), state machines (GameStatus, RoundStatus, ChallengeStatus), client offline action queue model (QueuedGameAction), DTOs and data privacy.",
|
||||
"seedPaths": [
|
||||
"SlipItIn.Server/Data/SlipItInDbContext.cs",
|
||||
"SlipItIn.Shared/DTOs/AuthDtos.cs",
|
||||
"SlipItIn.Shared/DTOs/GameStateDto.cs",
|
||||
"SlipItIn.Shared/Models/Game.cs",
|
||||
"SlipItIn.Shared/Models/GameRound.cs",
|
||||
"SlipItIn.Shared/Models/Phrase.cs",
|
||||
"SlipItIn.Shared/Models/Player.cs",
|
||||
"SlipItIn.Shared/Models/PlayerCard.cs",
|
||||
"SlipItIn.Shared/Models/SlipChallenge.cs",
|
||||
"SlipItIn.Shared/Models/User.cs",
|
||||
"SlipItIn/Models/QueuedGameAction.cs"
|
||||
],
|
||||
"relatedPages": [
|
||||
"/openwiki/architecture/overview.md",
|
||||
"/openwiki/operations/runbook.md",
|
||||
"/openwiki/quickstart.md",
|
||||
"/openwiki/source-map.md",
|
||||
"/openwiki/workflows/slip-and-challenge.md"
|
||||
],
|
||||
"instructions": [
|
||||
"Ensure all Mermaid diagrams follow mermaid-diagrams skill rules: no semicolons or pipes in labels, no unescaped angle brackets, short labels, captions below each diagram.",
|
||||
"Keep the ERD (mermaid erDiagram) as it accurately reflects the database schema defined in SlipItInDbContext.cs with correct entity relationships.",
|
||||
"Keep the GameStatus state diagram — it accurately shows Lobby -> InProgress -> Completed/Cancelled transitions.",
|
||||
"Keep the Guidance section with invariants: PlayerCard.IsUsed transitions from false to true, QueuedGameAction.ArgumentsJson must match GameHub method parameters.",
|
||||
"Keep the RoundStatus state diagram — it accurately shows Waiting -> Active -> Resolving -> Active/Completed transitions.",
|
||||
"Keep the frontmatter type: Domain Model and openwiki metadata.",
|
||||
"Language: English.",
|
||||
"Verify QueuedGameAction model description matches SlipItIn/Models/QueuedGameAction.cs.",
|
||||
"Verify all entity properties match the current code in SlipItIn.Shared/Models/. Key entities: User (Id, Username, Email, PasswordHash, IsActive), Game (Id, LobbyCode, HostId, Status, MaxPlayers=8, RoundDurationSeconds=45), Player (Id, UserId, GameId, Score, SuccessfulSlips, FailedSlips, IsReady, ConnectionId), Phrase (Id, Text, CreatorId, IsActive), GameRound (Id, GameId, RoundNumber, Status), PlayerCard (Id, PlayerId, PhraseId, GameRoundId, IsUsed), SlipChallenge (Id, GameRoundId, ChallengingPlayerId, TargetPlayerId, TargetCardId, Status).",
|
||||
"Verify the DTO table is accurate: GameStateDto (public, broadcast to group), PlayerHandDto (private, unicast to player), SlipChallengeDto (notification), AuthResponseDto (JWT token)."
|
||||
],
|
||||
"status": "skipped"
|
||||
},
|
||||
{
|
||||
"id": "fb04710f-8787-4249-a62e-5075a4284017",
|
||||
"path": "/openwiki/operations/runbook.md",
|
||||
"title": "Operations, Environment Setup & Testing Guidance",
|
||||
"purpose": "Operational guide for local development setup, .NET Aspire startup, EF Core PostgreSQL migrations, JWT configuration, and testing verification scenarios. Enhanced with actual test project documentation.",
|
||||
"seedPaths": [
|
||||
".github/workflows/build.yml",
|
||||
"SlipItIn.AppHost/AppHost.cs",
|
||||
"SlipItIn.Server.Tests/AuthControllerTests.cs",
|
||||
"SlipItIn.Server.Tests/DbSeederTests.cs",
|
||||
"SlipItIn.Server.Tests/GameHubTests.cs",
|
||||
"SlipItIn.Server.Tests/GameServiceTests.cs",
|
||||
"SlipItIn.Server.Tests/TestDbHelper.cs",
|
||||
"SlipItIn.Server/Program.cs",
|
||||
"SlipItIn.Server/appsettings.Development.json",
|
||||
"SlipItIn.Server/appsettings.json",
|
||||
"SlipItIn/MauiProgram.cs"
|
||||
],
|
||||
"relatedPages": [
|
||||
"/openwiki/architecture/overview.md",
|
||||
"/openwiki/domain/game-mechanics.md",
|
||||
"/openwiki/quickstart.md",
|
||||
"/openwiki/source-map.md",
|
||||
"/openwiki/workflows/slip-and-challenge.md"
|
||||
],
|
||||
"instructions": [
|
||||
"Add or enhance the Testing section to document the actual test project structure: SlipItIn.Server.Tests uses xUnit, Moq, EF Core InMemory database. Key test files: TestDbHelper.cs (creates isolated InMemory DbContexts, seeds users and games), GameServiceTests.cs (tests business logic), GameHubTests.cs (tests SignalR hub with mocked dependencies), AuthControllerTests.cs (tests registration/login), DbSeederTests.cs (tests database seeding).",
|
||||
"Ensure all Mermaid diagrams (if any) follow mermaid-diagrams skill rules.",
|
||||
"Keep the 6 testing verification scenarios (JWT Authentication, Player Access, MVVM Messaging, Offline Queueing, Dual-Layer Storage, Concurrency) — they are accurate and useful.",
|
||||
"Keep the Configuration & Secrets Management section — it accurately describes Jwt:Key (>= 32 chars), Jwt:Issuer, Jwt:Audience, Jwt:ExpirationMinutes (1440 default), ConnectionStrings:postgresdb.",
|
||||
"Keep the Entity Framework Core Migrations section — it accurately describes dotnet ef migrations add and dotnet ef database update commands.",
|
||||
"Keep the Guidance section with invariants: always run migrations using SlipItIn.Server as both project and startup-project.",
|
||||
"Keep the Local Development Environment Setup section — it accurately describes .NET 10 SDK, Docker/Podman, workload install, and Aspire startup command.",
|
||||
"Keep the frontmatter type: Runbook and openwiki metadata.",
|
||||
"Language: English.",
|
||||
"Verify the build command matches .github/workflows/build.yml: dotnet build SlipItIn.slnx (Release for server stack, Debug for MAUI), dotnet test SlipItIn.Server.Tests."
|
||||
],
|
||||
"status": "complete",
|
||||
"completedBy": "openwiki/0.5.0"
|
||||
},
|
||||
{
|
||||
"id": "9b660423-d759-41a0-8025-89aaf509ffc3",
|
||||
"path": "/openwiki/source-map.md",
|
||||
"title": "Source Code Map & Navigation Directory",
|
||||
"purpose": "Practical navigation guide mapping every source file across projects to system domains, responsibilities, and project structure. Used by engineers and agents to quickly locate code.",
|
||||
"seedPaths": [
|
||||
".github/workflows/build.yml",
|
||||
".github/workflows/openwiki-update.yml",
|
||||
"SlipItIn.AppHost/AppHost.cs",
|
||||
"SlipItIn.Server/Controllers/",
|
||||
"SlipItIn.Server/Data/",
|
||||
"SlipItIn.Server/Hubs/",
|
||||
"SlipItIn.Server/Migrations/",
|
||||
"SlipItIn.Server/Program.cs",
|
||||
"SlipItIn.Server/Services/",
|
||||
"SlipItIn.Shared/DTOs/",
|
||||
"SlipItIn.Shared/Models/",
|
||||
"SlipItIn.slnx",
|
||||
"SlipItIn/MauiProgram.cs",
|
||||
"SlipItIn/Services/",
|
||||
"SlipItIn/Services/Interfaces/",
|
||||
"SlipItIn/ViewModels/",
|
||||
"SlipItIn/Views/"
|
||||
],
|
||||
"relatedPages": [
|
||||
"/openwiki/architecture/overview.md",
|
||||
"/openwiki/domain/game-mechanics.md",
|
||||
"/openwiki/operations/runbook.md",
|
||||
"/openwiki/quickstart.md",
|
||||
"/openwiki/workflows/slip-and-challenge.md"
|
||||
],
|
||||
"instructions": [
|
||||
"Ensure all file descriptions are accurate and up-to-date. Verify that all file paths and class names match the current codebase.",
|
||||
"Keep the Automation & CI/CD section referencing .github/workflows/ — it is accurate.",
|
||||
"Keep the Solution Projects section (SlipItIn.slnx) accurate — it lists 6 projects.",
|
||||
"Keep the directory and source file map for each project (AppHost, ServiceDefaults, Server, Shared, Client).",
|
||||
"Keep the existing structure and formatting — do not change the document organization.",
|
||||
"Keep the frontmatter type: Reference and openwiki metadata.",
|
||||
"Language: English.",
|
||||
"Remove the Agents/ directory section entirely — this directory no longer exists in the repository. Also remove all references to Agents/Architecture.md and Agents/ProjectPlan.md throughout the document.",
|
||||
"Verify the MAUI Client section references all current files: MauiProgram.cs, Services/ (SignalRService, GameStateService, LocalStorageService, AuthSessionService, ApiService, AppConfigurationService), ViewModels/ (BaseViewModel, LoginViewModel, RegisterViewModel, LobbyViewModel, GameBoardViewModel), Views/ (LoginPage, RegisterPage, LobbyPage, GameBoardPage, ChallengeNotificationOverlay), Messages/ (LobbyCreatedMessage, GameStateChangedMessage, GameStartedMessage, PlayerHandChangedMessage, ChallengeReceivedMessage, ErrorOccurredMessage, ConnectionStateChangedMessage, SyncStateChangedMessage), Models/ (QueuedGameAction), Infrastructure/ (ServiceHelper)."
|
||||
],
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"id": "51247bfa-2aa0-47d2-9390-81376ea27202",
|
||||
"path": "/openwiki/workflows/slip-and-challenge.md",
|
||||
"title": "Slip & Challenge Workflows",
|
||||
"purpose": "Real-time game loops covering authentication, lobby creation, card dealing, phrase slipping, slip challenging, challenge resolution, offline action queuing, and SignalR hub event reference.",
|
||||
"seedPaths": [
|
||||
"SlipItIn.Server/Hubs/GameHub.cs",
|
||||
"SlipItIn.Server/Services/GameService.cs",
|
||||
"SlipItIn/Services/GameStateService.cs",
|
||||
"SlipItIn/Services/Interfaces/IGameStateService.cs",
|
||||
"SlipItIn/Services/Interfaces/ISignalRService.cs",
|
||||
"SlipItIn/Services/SignalRService.cs",
|
||||
"SlipItIn/ViewModels/GameBoardViewModel.cs",
|
||||
"SlipItIn/ViewModels/LobbyViewModel.cs"
|
||||
],
|
||||
"relatedPages": [
|
||||
"/openwiki/architecture/overview.md",
|
||||
"/openwiki/domain/game-mechanics.md",
|
||||
"/openwiki/operations/runbook.md",
|
||||
"/openwiki/quickstart.md",
|
||||
"/openwiki/source-map.md"
|
||||
],
|
||||
"instructions": [
|
||||
"Ensure all Mermaid diagrams follow mermaid-diagrams skill rules: no semicolons or pipes in labels, no unescaped angle brackets, short labels, captions below each diagram.",
|
||||
"Keep the Game Setup & Lobby Workflow text-based flow diagram — it accurately shows the authentication and lobby creation sequence.",
|
||||
"Keep the Guidance section with invariants: FlushQueuedActionsAsync executes sequentially by CreatedAtUtc, ResolveChallenge throws UnauthorizedAccessException for non-target players.",
|
||||
"Keep the Phrase Slipping & Offline Queueing Workflow section — it accurately shows the online/offline branching logic.",
|
||||
"Keep the Slip Challenge & Resolution Workflow section — it accurately shows challenge creation, notification, and resolution with penalty logic.",
|
||||
"Keep the frontmatter type: Workflow and openwiki metadata.",
|
||||
"Keep the mermaid sequence diagram for Offline Action Queueing & Reconnection Resync — it accurately shows the offline queue, automatic reconnection, state request, and queued action replay.",
|
||||
"Keep the mermaid sequence diagram for Slip Challenge & Resolution — it accurately shows the challenger/slipper interaction with GameHub and GameService.",
|
||||
"Language: English.",
|
||||
"Verify the SignalR Hub Event Reference table is complete and accurate: LobbyCreated (Caller), PlayerJoined (Group), GameStateUpdated (Group), GameStarted (Group), PlayerHandUpdated (Unicast), SlipSubmitted (Group), SlipChallenged (Group), ChallengeReceived (Unicast), ChallengeResolved (Group), Error (Caller).",
|
||||
"Verify the workflow descriptions match the current code: StartGame requires host validation, DealCards creates 5 random phrases per player, SubmitSlip sets IsUsed=true, ResolveChallenge reassigns card on false accusation."
|
||||
],
|
||||
"status": "pending"
|
||||
},
|
||||
{
|
||||
"id": "6cb9e502-0484-4eba-a226-c4ac85c14543",
|
||||
"path": "/openwiki/quickstart.md",
|
||||
"title": "SlipItIn Code Wiki Quickstart",
|
||||
"purpose": "Entry point for the wiki. Provides system overview, architecture snapshot, core game loop, task routing table, key architectural rules, and backlog. Must route readers through the full wiki hierarchy. Full regeneration required: remove stale Agents/ references, add testing row, refresh navigation links.",
|
||||
"seedPaths": [
|
||||
"README.md",
|
||||
"SlipItIn.AppHost/AppHost.cs",
|
||||
"SlipItIn.Server/Program.cs",
|
||||
"SlipItIn.slnx",
|
||||
"SlipItIn/MauiProgram.cs",
|
||||
"openwiki/architecture/overview.md",
|
||||
"openwiki/domain/game-mechanics.md",
|
||||
"openwiki/operations/runbook.md",
|
||||
"openwiki/source-map.md",
|
||||
"openwiki/workflows/slip-and-challenge.md"
|
||||
],
|
||||
"relatedPages": [
|
||||
"/openwiki/architecture/overview.md",
|
||||
"/openwiki/domain/game-mechanics.md",
|
||||
"/openwiki/operations/runbook.md",
|
||||
"/openwiki/source-map.md",
|
||||
"/openwiki/workflows/slip-and-challenge.md"
|
||||
],
|
||||
"instructions": [
|
||||
"Ensure all navigation links point to correct wiki pages (no broken links).",
|
||||
"Full regeneration from scratch based on the current repository state.",
|
||||
"Include a Backlog section (without Agents/ references) — mention the round timer engine as a pending feature.",
|
||||
"Include a Key Architectural Rules section with the 5 rules: IDbContextFactory thread safety, data privacy isolation, server-side validation, decoupled client messaging, dual-layer local storage.",
|
||||
"Include a core game loop section (authentication, lobby creation/joining, card dealing, slip submission, challenge resolution).",
|
||||
"Include a system overview with project structure tree (6 projects in SlipItIn.slnx).",
|
||||
"Include a task routing table covering: MAUI Client ViewModels & Pages, Client SignalR & Messaging, Offline Action Queue & Resync, Local Storage & Auth Session, Backend Game Engine & Hub, Domain Models & DTOs, Aspire & Infrastructure Setup, Server Testing & Test Helpers.",
|
||||
"Keep the system architecture mermaid diagram (graph TD) and ensure it accurately reflects the current architecture.",
|
||||
"Language: English. Document is the wiki entry point.",
|
||||
"Preserve the frontmatter with type: Overview and openwiki metadata.",
|
||||
"Use the mermaid-diagrams skill for any diagrams: ground in source, give each diagram a caption, use safe syntax."
|
||||
],
|
||||
"status": "pending"
|
||||
}
|
||||
],
|
||||
"deletePages": []
|
||||
}
|
||||
}
|
||||
@@ -1,14 +1,17 @@
|
||||
---
|
||||
type: Architecture
|
||||
title: System Architecture & Security Overview
|
||||
description: Technical architecture of SlipItIn, covering .NET Aspire orchestration, ASP.NET Core SignalR hub, .NET MAUI MVVM client layer, WeakReferenceMessenger event routing, and dual-layer local storage.
|
||||
tags: [architecture, spire, signalr, jwt, efcore, security, maui, mvvm]
|
||||
description: Technical architecture of SlipItIn, covering .NET Aspire orchestration, ASP.NET Core SignalR hub, .NET MAUI MVVM client layer, WeakReferenceMessenger event routing, dual-layer local storage, and concurrency safety via IDbContextFactory.
|
||||
tags: [architecture, aspire, signalr, jwt, efcore, security, maui, mvvm, concurrency, privacy]
|
||||
openwiki:
|
||||
roles: [architecture, domain]
|
||||
change_kinds: [lifecycle, public-api]
|
||||
source_paths: [SlipItIn/MauiProgram.cs, SlipItIn/Services/SignalRService.cs, SlipItIn/Services/GameStateService.cs, SlipItIn.Server/Program.cs, SlipItIn.Server/Hubs/GameHub.cs]
|
||||
symbols: [SignalRService, GameStateService, LocalStorageService, GameHub, GameService, IDbContextFactory]
|
||||
validation_commands: ["dotnet build SlipItIn.slnx"]
|
||||
verified:
|
||||
- by: openwiki/0.5.0
|
||||
at: 2026-09-07T15:04:46.288Z
|
||||
---
|
||||
|
||||
# System Architecture & Security Overview
|
||||
@@ -25,11 +28,11 @@ The solution uses **.NET Aspire** to orchestrate application resources, services
|
||||
|
||||
- **AppHost (`SlipItIn.AppHost/AppHost.cs`)**:
|
||||
- Provisions a PostgreSQL database container (`AddPostgres("pgsql").AddDatabase("postgresdb")`).
|
||||
- Registers the backend project `SlipItIn.Server` with a direct reference to the PostgreSQL resource.
|
||||
- Registers the client project `SlipItIn` with service discovery references to `SlipItIn.Server`.
|
||||
- Registers the backend project `SlipItIn.Server` with a direct reference to the PostgreSQL resource, plus `WaitFor(db)` to ensure the database is ready before the server starts.
|
||||
- Registers the client project `SlipItIn` with service discovery references to `SlipItIn.Server` via `WithReference(server)`.
|
||||
- **Service Defaults (`SlipItIn.ServiceDefaults/Extensions.cs`)**:
|
||||
- Configures **OpenTelemetry** logging, metrics (AspNetCore, HttpClient, Runtime), and tracing.
|
||||
- Exposes standardized health check endpoints (`/health` and `/alive`).
|
||||
- Configures **OpenTelemetry** logging (with `IncludeFormattedMessage` and `IncludeScopes`), metrics (AspNetCore, HttpClient, Runtime), and tracing.
|
||||
- Exposes standardized health check endpoints (`/health` and `/alive`), filtering health paths from tracing.
|
||||
- Enforces automatic HTTP resilience (`AddStandardResilienceHandler()`) and service discovery.
|
||||
|
||||
---
|
||||
@@ -42,16 +45,19 @@ The backend is an ASP.NET Core Web API & SignalR application providing REST endp
|
||||
|
||||
1. **`Program.cs`**:
|
||||
- Registers `AddServiceDefaults()`, `AddNpgsqlDataSource("postgresdb")`, and `AddDbContextFactory<SlipItInDbContext>()`.
|
||||
- Configures JWT Bearer authentication with custom `OnMessageReceived` token resolution for SignalR.
|
||||
- Automatically executes database migrations (`db.Database.Migrate()`) at startup.
|
||||
- Configures JWT Bearer authentication with custom `OnMessageReceived` token resolution for SignalR and `OnTokenValidated` for user existence checks.
|
||||
- Adds SignalR, CORS (`AllowAll`), controllers, and OpenAPI.
|
||||
- Automatically executes database migrations (`db.Database.Migrate()`) and seeds test data (`DbSeeder.SeedAsync()`) at startup.
|
||||
2. **`AuthController.cs`**:
|
||||
- Manages user registration (`/api/auth/register`), authentication (`/api/auth/login`), and profile retrieval (`/api/auth/me`).
|
||||
- Uses `BCrypt.Net` for secure password hashing.
|
||||
- Issues JWT tokens signed with `Jwt:Key`, containing `ClaimTypes.NameIdentifier`, `ClaimTypes.Name`, and `ClaimTypes.Email`.
|
||||
- Uses `IDbContextFactory` for thread-safe database access per request.
|
||||
3. **`GameHub.cs`**:
|
||||
- Real-time SignalR hub mapped to `/hubs/game`.
|
||||
- Annotated with `[Authorize]` to reject unauthenticated WebSocket connections.
|
||||
- Extracts and verifies user claims, delegating state mutations to `IGameService`.
|
||||
- Manages `ConnectionId` lifecycle: stores it on `OnConnectedAsync` (auto-rejoining the lobby group), and clears it on `OnDisconnectedAsync`.
|
||||
4. **`GameService.cs`**:
|
||||
- Implements core business logic: game creation, player joins, card dealing, slip submission, challenge creation, and resolution.
|
||||
- Adds `hostPlayer` directly to `game.Players` before `context.Games.Add(game)` to ensure EF Core navigation tracking consistency.
|
||||
@@ -81,12 +87,16 @@ options.Events = new JwtBearerEvents
|
||||
};
|
||||
```
|
||||
|
||||
### Token Validation
|
||||
`OnTokenValidated` extracts the `NameIdentifier` claim, creates a short-lived `DbContext` via `IDbContextFactory`, and verifies the user still exists and has `IsActive == true`. If the user is missing or inactive, the token is rejected before any hub method runs.
|
||||
|
||||
### Claims Validation & Access Control in `GameHub.cs`
|
||||
Inside `GameHub.cs`, claims are extracted directly from the authenticated SignalR context:
|
||||
|
||||
* `GetAuthenticatedUserId()`: Extracts `Context.User?.FindFirst(ClaimTypes.NameIdentifier)`. Throws `UnauthorizedAccessException` if missing or invalid.
|
||||
* `ValidatePlayerAccessAsync(playerId, authUserId)`: Queries the database to verify that `Player.UserId` matches the authenticated user ID.
|
||||
* `ValidateHostAccessAsync(gameId, authUserId)`: Verifies that `Game.HostId` matches the authenticated user ID before allowing lobby configuration or game start.
|
||||
* `ValidateUserInGameAsync(gameId, authUserId)`: Verifies the authenticated user is a participant in the specified game, used by `RequestGameState`.
|
||||
* `ValidateChallengeTargetAccessAsync(challengeId, authUserId)`: Ensures only the accused player can resolve a challenge against them.
|
||||
|
||||
### Real-Time Auth Flow Diagram
|
||||
@@ -101,14 +111,14 @@ sequenceDiagram
|
||||
participant DB as SlipItInDbContext
|
||||
|
||||
Client->>Auth: POST /api/auth/login
|
||||
Auth->>DB: Query User & Verify BCrypt Hash
|
||||
Auth-->>Client: 200 OK (JWT Access Token)
|
||||
Auth->>DB: Query User and Verify BCrypt Hash
|
||||
Auth-->>Client: 200 OK with JWT Access Token
|
||||
Client->>Hub: Connect WebSocket /hubs/game?access_token=JWT
|
||||
Hub->>Hub: Validate JWT Signature & Claims
|
||||
Client->>Hub: JoinLobby(lobbyCode)
|
||||
Hub->>Hub: GetAuthenticatedUserId()
|
||||
Hub->>Service: JoinGameAsync(lobbyCode, userId, connectionId)
|
||||
Hub-->>Client: Broadcast PlayerJoined (GameStateDto)
|
||||
Hub->>Hub: Validate JWT Signature, Claims, and User Active Status
|
||||
Client->>Hub: JoinLobby with lobbyCode
|
||||
Hub->>Hub: GetAuthenticatedUserId
|
||||
Hub->>Service: JoinGameAsync with lobbyCode, userId, connectionId
|
||||
Hub-->>Client: Broadcast PlayerJoined with GameStateDto
|
||||
```
|
||||
*Sequence diagram showing user authentication via REST and subsequent authenticated SignalR WebSocket connection.*
|
||||
|
||||
@@ -120,7 +130,7 @@ The cross-platform client app uses the **MVVM Pattern** built on `CommunityToolk
|
||||
|
||||
```mermaid
|
||||
graph TD
|
||||
subgraph UI_Layer["UI Layer (Views)"]
|
||||
subgraph UI_Layer["UI Layer - Views"]
|
||||
LoginPage["LoginPage"]
|
||||
RegisterPage["RegisterPage"]
|
||||
LobbyPage["LobbyPage"]
|
||||
@@ -136,7 +146,7 @@ graph TD
|
||||
|
||||
subgraph Messaging_Layer["Messaging Layer"]
|
||||
Messenger["WeakReferenceMessenger"]
|
||||
Messages["Messages (GameStateChanged, PlayerHandChanged, ChallengeReceived)"]
|
||||
Messages["Messages including GameStateChanged, PlayerHandChanged, ChallengeReceived, SyncStateChanged"]
|
||||
end
|
||||
|
||||
subgraph Service_Layer["Service Layer"]
|
||||
@@ -145,6 +155,7 @@ graph TD
|
||||
AuthSessionSvc["AuthSessionService"]
|
||||
ApiSvc["ApiService"]
|
||||
LocalStorageSvc["LocalStorageService"]
|
||||
AppConfigSvc["AppConfigurationService"]
|
||||
end
|
||||
|
||||
LoginPage --> LoginVM
|
||||
@@ -167,7 +178,7 @@ graph TD
|
||||
### Dependency Injection & Service Registration (`MauiProgram.cs`)
|
||||
- **Singletons**: `IAppConfigurationService` (`AppConfigurationService`), `ILocalStorageService` (`LocalStorageService`), `IAuthSessionService` (`AuthSessionService`), `IApiService` (`ApiService`), `ISignalRService` (`SignalRService`), `IGameStateService` (`GameStateService`), `LobbyViewModel`, `GameBoardViewModel`.
|
||||
- **Transients**: `LoginViewModel`, `RegisterViewModel`.
|
||||
- **Service Locator Helper**: `ServiceHelper.Services` captures `app.Services` at startup to allow static access where constructor DI is unavailable.
|
||||
- **Service Locator Helper**: `ServiceHelper.Services` captures `app.Services` at startup to allow static access where constructor DI is unavailable. Provides `GetRequiredService<T>()` for use in contexts like `WeakReferenceMessenger` handlers.
|
||||
|
||||
### Decoupled Messaging Infrastructure (`WeakReferenceMessenger`)
|
||||
To avoid memory leaks caused by long-lived event subscriptions in ViewModels, `GameStateService` translates raw `ISignalRService` hub events into strongly-typed messages dispatched via `WeakReferenceMessenger.Default`:
|
||||
@@ -179,6 +190,7 @@ To avoid memory leaks caused by long-lived event subscriptions in ViewModels, `G
|
||||
- `ChallengeReceivedMessage`: Dispatched when a player is directly targeted by a slip challenge.
|
||||
- `ErrorOccurredMessage`: Dispatched when a hub or network error occurs.
|
||||
- `ConnectionStateChangedMessage`: Dispatched when SignalR connection drops or reconnects.
|
||||
- `SyncStateChangedMessage`: Dispatched when the sync status changes (syncing, idle, queue count). Carries a `SyncStateInfo` record with `IsSyncing`, `StatusText`, and `PendingActions` fields.
|
||||
|
||||
ViewModels implement `IRecipient<TMessage>` and register via `WeakReferenceMessenger.Default.RegisterAll(this)` to receive automated UI updates.
|
||||
|
||||
@@ -189,13 +201,20 @@ ViewModels implement `IRecipient<TMessage>` and register via `WeakReferenceMesse
|
||||
_hubConnection = new HubConnectionBuilder()
|
||||
.WithUrl(_configuration.HubUrl, options =>
|
||||
{
|
||||
options.AccessTokenProvider = () => Task.FromResult<string?>(token);
|
||||
options.AccessTokenProvider = () => Task.FromResult(_authSession.AccessToken);
|
||||
})
|
||||
.WithAutomaticReconnect([TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30)])
|
||||
.WithAutomaticReconnect(new[] {TimeSpan.Zero, TimeSpan.FromSeconds(2), TimeSpan.FromSeconds(10), TimeSpan.FromSeconds(30)})
|
||||
.Build();
|
||||
```
|
||||
|
||||
When connection drops, `SignalRService` emits `ConnectionStateChanged`. Upon reconnection, `GameStateService` automatically triggers `ResyncAsync()` to fetch current state via `RequestGameStateAsync()` and flush locally queued offline actions.
|
||||
When connection drops, `SignalRService` emits `ConnectionStateChanged` with `false`. Upon reconnection, it emits `ConnectionStateChanged` with `true`, and `GameStateService` automatically triggers `ResyncAsync()` to fetch current state via `RequestGameStateAsync()` and flush locally queued offline actions.
|
||||
|
||||
The `AccessTokenProvider` reads `_authSession.AccessToken` at invocation time (not cached at connection build time), ensuring the latest token is used on reconnects. The `SemaphoreSlim _connectLock` prevents concurrent connect/disconnect operations.
|
||||
|
||||
On the server side, `GameHub.OnConnectedAsync` automatically detects if the reconnecting user was already in a game (Lobby or InProgress), stores the new `ConnectionId`, and re-joins the SignalR lobby group. `OnDisconnectedAsync` clears the `ConnectionId` for all affected players to prevent messages from being sent to dead connections.
|
||||
|
||||
### Offline Action Queue with Retry
|
||||
When the SignalR connection is unavailable, `GameStateService` provides `EnqueueActionAsync(method, arguments)` to persist actions locally. Each action stores the hub method name and serialized arguments as JSON. On reconnect, `FlushQueuedActionsCoreAsync` replays actions sequentially through `SignalRService.SendQueuedActionAsync`. Failed actions retry up to `MaxQueueRetries` (3) times; actions exceeding the retry limit are dropped and an error message is dispatched. The queue is persisted to `LocalStorageService` after each modification.
|
||||
|
||||
### Dual-Layer Storage Security Model (`LocalStorageService`)
|
||||
To satisfy security and privacy constraints:
|
||||
@@ -212,6 +231,7 @@ SignalR hubs process concurrent requests from multiple clients over persistent c
|
||||
SlipItIn solves this by registering EF Core with `AddDbContextFactory<SlipItInDbContext>`:
|
||||
|
||||
* Every method call inside `GameHub.cs`, `AuthController.cs`, and `GameService.cs` creates a short-lived, isolated `DbContext` session using `using var context = _contextFactory.CreateDbContext()`.
|
||||
* `OnTokenValidated` in Program.cs uses `CreateDbContextAsync` to verify user existence during JWT validation.
|
||||
* **Benefit**: Thread-safe database operations across simultaneous challenges, card transfers, and lobby updates without race conditions.
|
||||
|
||||
---
|
||||
@@ -220,8 +240,8 @@ SlipItIn solves this by registering EF Core with `AddDbContextFactory<SlipItInDb
|
||||
|
||||
To prevent players from inspecting opponent cards via network trace analysis, SlipItIn strictly separates public and private data DTOs:
|
||||
|
||||
* **`GameStateDto` (Public)**: Broadcast to all players in a lobby. Contains public game information (`GameId`, `LobbyCode`, `Status`, `CurrentRound`, `RoundTimeRemaining`) and player summaries (`PlayerId`, `Username`, `Score`, `IsReady`, `CardCount`). **Crucially, no card text is included.**
|
||||
* **`PlayerHandDto` (Private)**: Direct unicast message sent *only* to the specific player's SignalR `ConnectionId`. Contains card text (`CardId`, `PhraseId`, `Text`, `IsUsed`).
|
||||
* **`GameStateDto` (Public)**: Broadcast to all players in a lobby. Contains public game information (`GameId`, `LobbyCode`, `Status`, `CurrentRound`, `RoundTimeRemaining`) and player summaries (`PlayerId`, `Username`, `Score`, `IsReady`, `CardCount`). **Crucially, no card text is included.** The `CardCount` field represents only active (unused) cards.
|
||||
* **`PlayerHandDto` (Private)**: Direct unicast message sent *only* to the specific player's SignalR `ConnectionId` via `Clients.Client(connectionId).SendAsync("PlayerHandUpdated", hand)`. Contains card text (`CardId`, `PhraseId`, `Text`, `IsUsed`). Only cards from the active round are included.
|
||||
|
||||
This architecture guarantees that players cannot see opponent phrase cards, even if they inspect raw WebSocket packets.
|
||||
|
||||
@@ -233,19 +253,24 @@ When extending or modifying the system architecture:
|
||||
|
||||
- **When to Consult**: Consult this document before adding new real-time SignalR methods, introducing client ViewModels, modifying storage mechanisms, or altering server authentication logic.
|
||||
- **Runtime Invariants**:
|
||||
- `GameHub` methods must validate caller claims via `GetAuthenticatedUserId()` and `ValidatePlayerAccessAsync()`.
|
||||
- `GameHub` methods must validate caller claims via `GetAuthenticatedUserId()` and the appropriate `Validate*Async` method before every state mutation.
|
||||
- Client ViewModels must consume `WeakReferenceMessenger` messages rather than subscribing directly to `ISignalRService` C# events.
|
||||
- JWT tokens must never be persisted in `Preferences`; always use `SecureStorage`.
|
||||
- `GameService` must use `IDbContextFactory` and create short-lived contexts — never inject a shared `DbContext`.
|
||||
- New `GameHub` methods must handle `UnauthorizedAccessException`, `InvalidOperationException`, and general `Exception` with appropriate error messages sent to the caller.
|
||||
- **Extension Points**:
|
||||
- New real-time server messages: Add method to `GameHub`, add event to `ISignalRService`, add message class in `SlipItIn/Messages/`, update `GameStateService` listener.
|
||||
- New ViewModels: Register as Transient (dialogs/auth) or Singleton (main navigation screens) in `MauiProgram.cs`.
|
||||
- New queued offline actions: Use `GameStateService.EnqueueActionAsync` with the hub method name and arguments.
|
||||
- **Primary Source Files**:
|
||||
- `SlipItIn/MauiProgram.cs`
|
||||
- `SlipItIn/Services/SignalRService.cs`
|
||||
- `SlipItIn/Services/GameStateService.cs`
|
||||
- `SlipItIn/Services/LocalStorageService.cs`
|
||||
- `SlipItIn.Server/Program.cs`
|
||||
- `SlipItIn.Server/Hubs/GameHub.cs`
|
||||
- `SlipItIn.Server/Services/GameService.cs`
|
||||
- `SlipItIn.Server/Controllers/AuthController.cs`
|
||||
- **Focused Checks**: Build and verify project references across client, server, and shared libraries.
|
||||
- **Minimal Validation Command**:
|
||||
```bash
|
||||
|
||||
@@ -1,14 +1,17 @@
|
||||
---
|
||||
type: Runbook
|
||||
title: Operations, Environment Setup & Testing Guidance
|
||||
description: Operational guide for launching SlipItIn with .NET Aspire, running EF Core PostgreSQL migrations, configuring secrets, and executing client/server verification tests.
|
||||
tags: [operations, runbook, spire, postgresql, migrations, testing, mvvm, offline-queue]
|
||||
description: Operational guide for local development setup, .NET Aspire startup, EF Core PostgreSQL migrations, JWT configuration, and testing verification scenarios.
|
||||
tags: [operations, runbook, aspire, postgresql, migrations, testing, xunit, signalr]
|
||||
openwiki:
|
||||
roles: [operations, testing]
|
||||
change_kinds: [lifecycle, public-api]
|
||||
source_paths: [SlipItIn.AppHost/AppHost.cs, SlipItIn.Server/Program.cs, SlipItIn/MauiProgram.cs]
|
||||
symbols: [AppHost, Program, MauiProgram]
|
||||
validation_commands: ["dotnet build SlipItIn.slnx"]
|
||||
source_paths: [SlipItIn.AppHost/AppHost.cs, SlipItIn.Server/Program.cs, SlipItIn/MauiProgram.cs, SlipItIn.Server.Tests/TestDbHelper.cs]
|
||||
symbols: [AppHost, Program, MauiProgram, TestDbHelper]
|
||||
validation_commands: ["dotnet build SlipItIn.slnx", "dotnet test SlipItIn.Server.Tests"]
|
||||
verified:
|
||||
- by: openwiki/0.5.0
|
||||
at: 2026-09-07T15:04:46.288Z
|
||||
---
|
||||
|
||||
# Operations, Environment Setup & Testing Guidance
|
||||
@@ -38,23 +41,24 @@ dotnet run --project SlipItIn.AppHost/SlipItIn.AppHost.csproj
|
||||
1. Aspire launches a PostgreSQL container (`pgsql`/`postgresdb`).
|
||||
2. Aspire builds and starts `SlipItIn.Server`.
|
||||
3. `Program.cs` automatically executes EF Core migrations (`db.Database.Migrate()`), creating database tables if they do not exist.
|
||||
4. Aspire Dashboard opens in your web browser, displaying live metrics, OpenTelemetry traces, and structured logs for all services.
|
||||
4. `DbSeeder.SeedAsync(db)` populates initial users and phrases when the database is empty.
|
||||
5. Aspire Dashboard opens in your web browser, displaying live metrics, OpenTelemetry traces, and structured logs for all services.
|
||||
|
||||
---
|
||||
|
||||
## 2. Configuration & Secrets Management
|
||||
|
||||
Configuration settings are loaded from `appsettings.json` and environment variables.
|
||||
Configuration settings are loaded from `appsettings.json`, `appsettings.Development.json`, and environment variables.
|
||||
|
||||
### Key Configuration Keys (`SlipItIn.Server`)
|
||||
|
||||
| Key | Default Value | Description |
|
||||
|---|---|---|
|
||||
| `Jwt:Key` | `YourSuperSecretKeyThatIsAtLeast32CharactersLong!` | Secret signing key for JWT tokens (Must be >= 256 bits). |
|
||||
| `Jwt:Key` | _(none in production; dev fallback: `DevOnlyKey-NotForProduction-ChangeMe-0123456789abcdef`)_ | Secret signing key for JWT tokens (Must be >= 32 characters / >= 256 bits). In non-development environments, a missing or short key causes a startup exception. |
|
||||
| `Jwt:Issuer` | `SlipItInServer` | Token issuer claim. |
|
||||
| `Jwt:Audience` | `SlipItInClient` | Token audience claim. |
|
||||
| `Jwt:ExpirationMinutes` | `1440` (24 hours) | JWT token lifespan. |
|
||||
| `ConnectionStrings:postgresdb` | Configured via Aspire | PostgreSQL connection string. |
|
||||
| `ConnectionStrings:postgresdb` | Configured via Aspire | PostgreSQL connection string injected by the Aspire orchestrator. |
|
||||
|
||||
### Production Configuration Security
|
||||
In production deployment, override `Jwt:Key` using environment variables or user secrets (`dotnet user-secrets`):
|
||||
@@ -85,7 +89,42 @@ dotnet ef database update --project SlipItIn.Server --startup-project SlipItIn.S
|
||||
|
||||
---
|
||||
|
||||
## 4. Testing Guidance & Verification Scenarios
|
||||
## 4. Test Project Structure
|
||||
|
||||
The `SlipItIn.Server.Tests` project contains all server-side automated tests. It targets `net10.0` and uses **xUnit** as the test framework, **Moq** for mocking dependencies, and **EF Core InMemory** (`Microsoft.EntityFrameworkCore.InMemory`) for isolated database contexts.
|
||||
|
||||
### Test Infrastructure
|
||||
|
||||
**`TestDbHelper.cs`** — Static helper class that creates isolated test databases and seeds test data:
|
||||
- `CreateFactory()` — Returns an `IDbContextFactory<SlipItInDbContext>` backed by an EF Core InMemory database with a unique name per test, ensuring complete test isolation.
|
||||
- `SeedUser(db, username, email, isActive)` — Inserts a minimal `User` entity and returns it.
|
||||
- `SeedPhrases(db, count)` — Inserts a set of `Phrase` entities (with an auto-created creator user) for card-dealing tests.
|
||||
- `SeedGame(db, host, status)` — Creates a `Game` with a host `Player` attached, returning both for test assertions.
|
||||
|
||||
### Test Files
|
||||
|
||||
| File | Class Under Test | What It Covers |
|
||||
|---|---|---|
|
||||
| `GameServiceTests.cs` | `GameService` | Business logic: game creation, joining, starting, player ready, card dealing, slip submission, challenge creation/resolution, game state queries with round time computation. |
|
||||
| `GameHubTests.cs` | `GameHub` | SignalR hub behavior: lobby creation/join, player ready, game start, slip submission, challenge submission/resolution, authorization enforcement. Uses Moq to mock `IGameService`, `IHubCallerClients`, `IGroupManager`, and `ISingleClientProxy`. InMemory DB is used only for the hub's own player-access validation. |
|
||||
| `AuthControllerTests.cs` | `AuthController` | Registration (token generation, input validation, case-insensitive duplicate detection), login (credential verification, missing-field handling), and the `/auth/me` claims endpoint. |
|
||||
| `DbSeederTests.cs` | `DbSeeder` | Seed idempotency: verifies `SeedAsync` creates users and phrases when the database is empty, but does nothing when data already exists, and is safe to call repeatedly. |
|
||||
|
||||
### Running Tests
|
||||
|
||||
```bash
|
||||
dotnet test SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj
|
||||
```
|
||||
|
||||
For CI-matching behavior (Release configuration):
|
||||
|
||||
```bash
|
||||
dotnet test SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj --configuration Release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 5. Testing Guidance & Verification Scenarios
|
||||
|
||||
When developing or extending SlipItIn features, verify the core architecture through these targeted test scenarios:
|
||||
|
||||
@@ -118,13 +157,18 @@ When developing or extending SlipItIn features, verify the core architecture thr
|
||||
|
||||
---
|
||||
|
||||
## 5. Guidance for Future Agents & Developers
|
||||
## 6. Guidance for Future Agents & Developers
|
||||
|
||||
- **When to Consult**: Refer to this runbook for local environment setup, Aspire startup commands, EF Core migration commands, and test verification procedures.
|
||||
- **Invariants**:
|
||||
- Always run migrations using `SlipItIn.Server` as both project and startup-project.
|
||||
- Test offline queue flushing before submitting client-side real-time changes.
|
||||
- **CI Build Commands** (from `.github/workflows/build.yml`):
|
||||
- Server stack: `dotnet build SlipItIn.Shared/SlipItIn.Shared.csproj --no-restore --configuration Release` (and similarly for `ServiceDefaults`, `Server`, `Server.Tests`, `AppHost`).
|
||||
- MAUI client: `dotnet build SlipItIn/SlipItIn.csproj --no-restore --configuration Debug`.
|
||||
- Server tests: `dotnet test SlipItIn.Server.Tests/SlipItIn.Server.Tests.csproj --no-restore --configuration Release`.
|
||||
- **Minimal Validation Command**:
|
||||
```bash
|
||||
dotnet build SlipItIn.slnx
|
||||
dotnet test SlipItIn.Server.Tests
|
||||
```
|
||||
|
||||
Reference in New Issue
Block a user