Reads RankInfoEntry.LowerLimitPoint from ranks.csv (via IGlobalsRepository)
for main-tier floors — no hardcoded thresholds. Master (Point=50000) and
Grand Master (MP-based, floor 5000) branch handles the post-Master path.
GetAsync is a read-only snapshot used later by AI-start's SelfInfo.
13 unit tests cover first-win, loss floors (0, Beginner→D, D→D0), win
crossing 50000, MP awards at Master, GM promotion at MP≥5000, GM floor
protecting against Master demotion, format separation, Crossover throw.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
One row per (viewer, format) tracking accumulated Point + MasterPoint.
Unique index on (ViewerId, Format) enforces one row per format per viewer
(mirrors owned-collection precedent per project_owned_collection_unique_index).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Doubles the shipped defaults across all modes without touching per-mode
overrides. Curve interaction against classexp.csv (L1=50, L2=150):
- Win (200 XP): crosses both L1 + L2 → land at L3, Exp=0
- Loss (50 XP): exactly meets L1 → land at L2, Exp=0
Updates every affected test assertion (Practice, Rank, Free, TK2,
Colosseum, Story) for the new level/exp math.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- StoryConfig.ClassXpPerClear retired; equivalent knob moves to
BattleXpConfig.StoryXpPerClear (falls back to XpPerWin when null).
- StoryService.FinishAsync now grants class XP via IBattleXpService with
BattleXpMode.Story on the firstClear && isPlayShape branch. Persists
post-grant Level/Exp to Viewer.Classes.
- Wire fields (get_class_experience, class_level) stringified as before.
- StoryConfig kept as an empty [ConfigSection] for future story-specific
knobs. StoryServiceTests updated to reflect the new default path
(XpPerWin=100 crosses classexp.csv L1=50 → L2 with 50 carry).
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- ColosseumBattleFinishResponseDto gains get_class_experience,
class_experience, class_level. Client-safe: same BattleFinishResponsProcessing
handles all three keys as ints (decomp verified).
- ArenaColosseumBattleController.FinishInternal grants win/loss XP via
IBattleXpService with BattleXpMode.Colosseum for both /colosseum_battle/finish
and /colosseum_rank_battle/finish URLs.
- Existing win test extended: XP wire assertions + persistence check.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- FreeBattleController.Finish (rotation + unlimited URLs) now grants
win/loss XP via IBattleXpService with BattleXpMode.Free.
- Preserves the strict field subset (no rank/master fields).
- Tests updated: win asserts XP+level-up; new loss test on rotation URL.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- RankBattleController.Finish (covers all 4 URLs: rotation, unlimited,
ai_rotation, ai_unlimited) now grants win/loss XP via IBattleXpService
with BattleXpMode.Rank. Uses req.ClassId from wire.
- Tests updated: AI-variant win asserts new XP values; consistency-loss
asserts loss XP. Added persistence test on /unlimited_rank_battle/finish.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- Practice finish now grants win/loss XP via IBattleXpService.GrantAsync
with BattleXpMode.Practice. Response fields (get_class_experience,
class_experience, class_level) reflect post-grant totals.
- Uses req.ClassId from wire; no BattleContext lookup needed.
- Test asserts default XpPerWin=100 crosses classexp.csv L1=50 threshold
→ Level=2 + Exp=50 carry, persists to Viewer.Classes.
- Added loss-XP test asserting default XpPerLoss=25 stays under threshold.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
- ArenaTwoPickService now delegates class-XP grants to IBattleXpService
with BattleXpMode.ArenaTwoPick. Inline GrantClassXp/ResolveClassLevel
helpers deleted.
- ClassXpPerBattle field removed from ArenaTwoPickConfig; TK2 XP now
configurable via ArenaTwoPickXpPerWin/XpPerLoss overrides or the global
BattleXpConfig defaults (100/25).
- Updated 5 test files (ArenaTwoPickService callers) for the new
constructor arg. Added loss-XP assertion to Finish tests.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds BattleXpConfig ([ConfigSection("BattleXp")]) with global
XpPerWin/XpPerLoss plus per-mode nullable overrides. Adds BattleXpMode
enum and IViewerRepository.LoadForBattleXpGrantAsync — focused tracked
load with viewer.Classes + Class nav ref included, for the service
about to be introduced.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Calls UseSvSimSerilog("svsim-content") right after CreateBuilder and
wraps Main in try/catch/finally. No environment gate needed —
ContentServer has no test-suite path. The existing manual request-log
middleware (uses ILogger<Program>) now flows through Serilog
automatically; swapping it for UseSerilogRequestLogging() is a
follow-up per the spec.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Registers Serilog via SVSim.Hosting.UseSvSimSerilog("svsim-api") right
after CreateBuilder and wraps Main in try/catch/finally so a fatal
startup exception logs before CloseAndFlush.
Also updates the SVSim.Hosting extension to gate the file sink on entry
assembly rather than the ASP.NET environment name. WebApplicationFactory
sets the environment via a hook that resolves after both CreateBuilder
and the UseSerilog callback see their environment view, so
IsEnvironment("Testing") is unreliable in tests. The entry assembly
under `dotnet test` is always "testhost", which is a reliable signal.
appsettings.json grows a Serilog section mirroring the existing
Logging:LogLevel overrides; the Logging block itself is left in place
per the spec's follow-up cleanup note.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
Adds new SVSim.Hosting project carrying Serilog + Serilog.AspNetCore +
Serilog.Settings.Configuration + enricher package refs, and a single
IHostBuilder extension method UseSvSimSerilog(appName) that installs a
colored console sink and a plain-text file sink rolling daily with a
100 MB per-file cap and 7-file retention. ReadFrom.Configuration is
called last so appsettings can override minimum levels per category.
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>