Skip to content
This repository was archived by the owner on Jun 5, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 9 commits
Commits
Show all changes
18 commits
Select commit Hold shift + click to select a range
2aa0e54
chore(mutation): add per-BC Stryker tooling and exclude i18n registra…
claude May 23, 2026
2995f9b
test(coaching): raise Domain mutation score 53%->95%, Application 92%…
claude May 23, 2026
0a333d7
test(hotfixes): cover HotfixId boundary invariant, Domain mutation 64…
claude May 23, 2026
b2f3f1b
test(encounter-knowledge): Domain mutation 43%->94% via sociable + bo…
claude May 23, 2026
b69a005
test(knowledge-mining): Domain mutation 56%->97%
claude May 23, 2026
95514a2
test(comparison): Domain 54%->96%, Application 77%->97%
claude May 23, 2026
d9aa66d
test(log-ingestion): Application mutation 45%->82%
claude May 23, 2026
e50296f
test(log-ingestion): Domain mutation 69%->82% via boundary VO tests
claude May 23, 2026
95e69c6
test(spell-knowledge): Domain mutation 47%->65% via boundary VO tests
claude May 23, 2026
b2ee937
test(analysis): Domain mutation 61%->90%; document quality bar in CLA…
claude May 23, 2026
2d483a3
test(spell-knowledge): Domain mutation 65%->91%, Application 92%
claude May 23, 2026
7cd28a1
test(log-ingestion): Domain mutation 82%->90%
claude May 23, 2026
ae11b69
test(log-ingestion): Application mutation 82%->95%
claude May 23, 2026
eaef5fa
test(identity): Domain mutation 80%->95%
claude May 24, 2026
146d5a2
test(kernel): mutation 44%->95%; fix broken mutate-scope exclusions
claude May 24, 2026
d856774
test(desktop): cover EncounterSegmenter metadata parsing + segmentation
claude May 24, 2026
2124519
test(coverage): align coverage scope with mutation scope; close line-…
claude May 24, 2026
fd73a67
refactor(knowledge-mining): remove dead attach-on-accept scaffolding
claude May 24, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions dotnet-tools.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"version": 1,
"isRoot": true,
"tools": {
"dotnet-stryker": {
"version": "4.14.2",

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🌐 Web query:

What is the latest version of dotnet-stryker NuGet package and is version 4.14.2 available?

💡 Result:

Latest version of the dotnet-stryker NuGet package is 4.14.1 [1]. Yes—version 4.14.2 is available: I could not find dotnet-stryker 4.14.2 on NuGet; the NuGet Gallery page lists 4.14.1 as the latest version, and no 4.14.2 entry was surfaced in the results I checked [1].

Citations:


🌐 Web query:

dotnet-stryker 4.14.2 NuGet available?

💡 Result:

No. As of 2026-05-23, the NuGet package for dotnet-stryker shows 4.14.1 as the latest available version; I could not find a NuGet package entry for dotnet-stryker 4.14.2 on nuget.org. What I found: - NuGet Gallery lists dotnet-stryker version 4.14.1, with install commands for the.NET tool. [1] - The Stryker repository release/tag I found for dotnet-stryker is 4.14.1 (not 4.14.2). [2] A GitHub PR exists titled “Bump dotnet-stryker from 4.14.1 to 4.14.2”, but that is not proof that 4.14.2 has been published to NuGet. [3]

Citations:


Update dotnet-stryker in dotnet-tools.json—NuGet doesn’t publish 4.14.2

NuGet Gallery lists dotnet-stryker latest as 4.14.1 and there’s no published package entry for 4.14.2, so dotnet-tools.json specifying 4.14.2 will likely fail tool installation. Update version to 4.14.1 (or another version that’s actually on nuget.org). [1]

[1] https://www.nuget.org/packages/dotnet-stryker

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@dotnet-tools.json` at line 6, The dotnet-tools.json entry for dotnet-stryker
specifies an unavailable version; update the "version" field for dotnet-stryker
from "4.14.2" to a valid published version (e.g., "4.14.1") so the tool can be
installed; locate the dotnet-stryker entry and change the "version" value
accordingly.

"commands": [
"dotnet-stryker"
],
"rollForward": false
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
namespace Coaching.Application.UnitTests.DomainTests;

public sealed class CoachingIdTests
{
[Fact]
public void Should_RejectEmptyGuid_When_PlayerCoachingProfileIdFromEmpty()
{
var act = () => PlayerCoachingProfileId.From(Guid.Empty);

act.Should().Throw<ArgumentException>();
}

[Fact]
public void Should_WrapValue_When_PlayerCoachingProfileIdFromGuid()
{
var guid = Guid.NewGuid();

PlayerCoachingProfileId.From(guid).Value.Should().Be(guid);
}

[Fact]
public void Should_BeEqual_When_PlayerCoachingProfileIdsShareValue()
{
var guid = Guid.NewGuid();

PlayerCoachingProfileId.From(guid).Should().Be(PlayerCoachingProfileId.From(guid));
}

[Fact]
public void Should_GenerateDistinctIds_When_PlayerCoachingProfileIdCreated()
{
PlayerCoachingProfileId.Create().Should().NotBe(PlayerCoachingProfileId.Create());
}

[Fact]
public void Should_RejectEmptyGuid_When_UserWeeklyProgressionIdFromEmpty()
{
var act = () => UserWeeklyProgressionId.From(Guid.Empty);

act.Should().Throw<ArgumentException>();
}

[Fact]
public void Should_BeEqual_When_UserWeeklyProgressionIdsShareValue()
{
var guid = Guid.NewGuid();

UserWeeklyProgressionId.From(guid).Should().Be(UserWeeklyProgressionId.From(guid));
}

[Fact]
public void Should_GenerateDistinctIds_When_UserWeeklyProgressionIdCreated()
{
UserWeeklyProgressionId.Create().Should().NotBe(UserWeeklyProgressionId.Create());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,87 @@ public void Should_RejectOutOfRangeWeek_When_WeekIsZero()
act.Should().Throw<ArgumentException>();
}

[Fact]
public void Should_RejectOutOfRangeWeek_When_WeekAbove53()
{
var act = () => IsoWeek.From(2026, 54);

act.Should().Throw<ArgumentException>();
}

[Theory]
[InlineData(1899)]
[InlineData(10000)]
public void Should_RejectOutOfRangeYear_When_YearOutsideSupportedRange(int year)
{
var act = () => IsoWeek.From(year, 10);

act.Should().Throw<ArgumentException>();
}

[Fact]
public void Should_AcceptBoundaryValues_When_YearAndWeekAtLimits()
{
IsoWeek.From(1900, 1).Should().NotBeNull();
IsoWeek.From(9999, 53).Should().NotBeNull();
}

[Fact]
public void Should_RollIntoNextWeek_When_NextIsCalled()
{
var week = IsoWeek.From(2026, 20);

var next = week.Next();

next.Year.Should().Be(2026);
next.Week.Should().Be(21);
}

[Fact]
public void Should_RollAcrossYearBoundary_When_NextIsCalledOnLastWeek()
{
var last = IsoWeek.FromDate(new DateOnly(2026, 12, 31));

var next = last.Next();

next.Year.Should().Be(2027);
}

[Fact]
public void Should_MapToContainingWeek_When_BuiltFromUtcInstant()
{
var week = IsoWeek.FromUtc(new DateTime(2026, 5, 21, 14, 30, 0, DateTimeKind.Utc));

week.Should().Be(IsoWeek.From(2026, 21));
}

[Fact]
public void Should_SpanMondayToSunday_When_StartAndEndRequested()
{
var week = IsoWeek.From(2026, 21);

week.StartDate().Should().Be(new DateOnly(2026, 5, 18));
week.EndDate().Should().Be(new DateOnly(2026, 5, 24));
}

[Fact]
public void Should_RankAfterNull_When_ComparedToNull()
{
IsoWeek.From(2026, 21).CompareTo(null).Should().BeGreaterThan(0);
}

[Fact]
public void Should_NotBeEqual_When_WeekDiffers()
{
IsoWeek.From(2026, 21).Should().NotBe(IsoWeek.From(2026, 22));
}

[Fact]
public void Should_NotBeEqual_When_YearDiffers()
{
IsoWeek.From(2026, 21).Should().NotBe(IsoWeek.From(2025, 21));
}

[Fact]
public void Should_BeEqual_When_SameYearAndWeek()
{
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
namespace Coaching.Application.UnitTests.DomainTests;

public sealed class LeverImpactEstimateTests
{
[Fact]
public void Should_ExposeBothDimensions_When_Constructed()
{
var estimate = new LeverImpactEstimate(12.5, 2.0);

estimate.EstimatedDpsGainPct.Should().Be(12.5);
estimate.EstimatedDeathsAvoided.Should().Be(2.0);
}

[Theory]
[InlineData(double.NaN, 1.0)]
[InlineData(double.PositiveInfinity, 1.0)]
[InlineData(1.0, double.NaN)]
[InlineData(1.0, double.NegativeInfinity)]
public void Should_RefuseConstruction_When_AnyDimensionIsNotFinite(double dps, double deaths)
{
var act = () => new LeverImpactEstimate(dps, deaths);

act.Should().Throw<ArgumentException>();
}

[Theory]
[InlineData(-0.1, 1.0)]
[InlineData(1.0, -0.1)]
public void Should_RefuseConstruction_When_AnyDimensionIsNegative(double dps, double deaths)
{
var act = () => new LeverImpactEstimate(dps, deaths);

act.Should().Throw<ArgumentException>();
}

[Fact]
public void Should_AllowZero_When_LeverHasNoEstimatedGain()
{
var estimate = new LeverImpactEstimate(0, 0);

estimate.EstimatedDpsGainPct.Should().Be(0);
estimate.EstimatedDeathsAvoided.Should().Be(0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
namespace Coaching.Application.UnitTests.DomainTests;

public sealed class StopsMetricsTests
{
private static StopsMetrics WithOneNegative(int index)
{
var v = new int[12];
for (var i = 0; i < v.Length; i++)
v[i] = 1;
v[index] = -1;
Comment on lines +7 to +10

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify language-version support and locate non-collection-expression initializations in this test file.

fd -e csproj | xargs rg -n "<LangVersion>|<TargetFramework>"
rg -n "new int\\[12\\]" src/Coaching/Coaching.Application.UnitTests/DomainTests/StopsMetricsTests.cs

Repository: Leroy-Florian/WOW

Length of output: 296


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Locate the unit test csproj and check its C# version settings
TEST_CSPROJ="$(fd -e csproj "Coaching.Application.UnitTests" -a src | head -n 1 || true)"
echo "TEST_CSPROJ=$TEST_CSPROJ"

if [ -n "${TEST_CSPROJ:-}" ]; then
  echo "== LangVersion in test csproj =="
  rg -n "<LangVersion>|<TargetFramework>|<LangVersion>" "$TEST_CSPROJ" || true
fi

echo "== Any LangVersion anywhere in repo (csproj only) =="
rg -n --glob "*.csproj" "<LangVersion>" src || true

echo "== Look for existing collection expressions usage =="
# Heuristic: find `var x = [` or `int[] x = [` etc.
rg -n --glob "*.cs" --glob "!**/*.Designer.cs" "=\s*\[" src | head -n 50

Repository: Leroy-Florian/WOW

Length of output: 7674


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== Repo-root global.json (if any) =="
find . -maxdepth 3 -name "global.json" -print -exec cat {} \;

echo "== Directory.Build.props (if any) containing LangVersion or C# settings =="
find . -maxdepth 4 -name "Directory.Build.props" -print
for f in $(find . -maxdepth 4 -name "Directory.Build.props"); do
  echo "--- $f ---"
  rg -n "LangVersion|CSharp|<LangVersion>|<TargetFramework>|LangVersion" "$f" || true
done

echo "== Any other build props targets with LangVersion =="
rg -n --glob "**/*.props" --glob "**/*.targets" "<LangVersion>|LangVersion" . || true

Repository: Leroy-Florian/WOW

Length of output: 503


Replace the new int[12] + fill loop with a C# collection expression

Directory.Build.props sets LangVersion to 14.0, and this codebase uses collection expressions ([])—so update this array initialization to match the C# guideline.

♻️ Proposed fix
-        var v = new int[12];
-        for (var i = 0; i < v.Length; i++)
-            v[i] = 1;
+        int[] v = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
         v[index] = -1;
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var v = new int[12];
for (var i = 0; i < v.Length; i++)
v[i] = 1;
v[index] = -1;
int[] v = [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1];
v[index] = -1;
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/Coaching/Coaching.Application.UnitTests/DomainTests/StopsMetricsTests.cs`
around lines 7 - 10, The code creates and fills an int array with new int[12]
plus a loop; replace that with a C# collection expression to initialize the
12-element array concisely and then set the single negative element: construct v
using a collection expression (e.g., an array literal or repeated value
expression) instead of new int[12] and the for-loop, then assign v[index] = -1;
update the variable v and preserve the existing use of index and its bounds.

return new StopsMetrics(
v[0], v[1], v[2], v[3], v[4], v[5],
v[6], v[7], v[8], v[9], v[10], v[11]);
}

[Theory]
[InlineData(0)]
[InlineData(1)]
[InlineData(2)]
[InlineData(3)]
[InlineData(4)]
[InlineData(5)]
[InlineData(6)]
[InlineData(7)]
[InlineData(8)]
[InlineData(9)]
[InlineData(10)]
[InlineData(11)]
public void Should_RefuseConstruction_When_AnyCountIsNegative(int negativeField)
{
var act = () => WithOneNegative(negativeField);

act.Should().Throw<ArgumentException>();
}

[Fact]
public void Should_ExposeEveryCount_When_Constructed()
{
var metrics = new StopsMetrics(
totalStops: 1, kickCount: 2, crowdControlCount: 3,
correctStopCount: 4, acceptableStopCount: 5, wastedKickCount: 6,
successfulStopCount: 7, lateNoEffectCount: 8, immuneCount: 9,
overcappedStopCount: 10, missedOpportunityCount: 11, runCount: 12);

metrics.TotalStops.Should().Be(1);
metrics.KickCount.Should().Be(2);
metrics.CrowdControlCount.Should().Be(3);
metrics.CorrectStopCount.Should().Be(4);
metrics.AcceptableStopCount.Should().Be(5);
metrics.WastedKickCount.Should().Be(6);
metrics.SuccessfulStopCount.Should().Be(7);
metrics.LateNoEffectCount.Should().Be(8);
metrics.ImmuneCount.Should().Be(9);
metrics.OvercappedStopCount.Should().Be(10);
metrics.MissedOpportunityCount.Should().Be(11);
metrics.RunCount.Should().Be(12);
}

[Fact]
public void Should_BeAllZero_When_Empty()
{
var empty = StopsMetrics.Empty;

empty.TotalStops.Should().Be(0);
empty.KickCount.Should().Be(0);
empty.CrowdControlCount.Should().Be(0);
empty.CorrectStopCount.Should().Be(0);
empty.AcceptableStopCount.Should().Be(0);
empty.WastedKickCount.Should().Be(0);
empty.SuccessfulStopCount.Should().Be(0);
empty.LateNoEffectCount.Should().Be(0);
empty.ImmuneCount.Should().Be(0);
empty.OvercappedStopCount.Should().Be(0);
empty.MissedOpportunityCount.Should().Be(0);
empty.RunCount.Should().Be(0);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
namespace Coaching.Application.UnitTests.DomainTests;

public sealed class UseCaseKeyTests
{
[Theory]
[InlineData("")]
[InlineData(" ")]
public void Should_RefuseConstruction_When_Blank(string value)
{
var act = () => UseCaseKey.From(value);

act.Should().Throw<ArgumentException>();
}

[Fact]
public void Should_RefuseConstruction_When_LongerThanMaxLength()
{
var tooLong = new string('a', UseCaseKey.MaxLength + 1);

var act = () => UseCaseKey.From(tooLong);

act.Should().Throw<ArgumentException>();
}

[Theory]
[InlineData("Kick Uptime")]
[InlineData("kick_uptime")]
[InlineData("-kick")]
[InlineData("kick--uptime")]
[InlineData("kické")]
public void Should_RefuseConstruction_When_NotKebabCaseAscii(string value)
{
var act = () => UseCaseKey.From(value);

act.Should().Throw<ArgumentException>();
}

[Fact]
public void Should_NormalizeToLowerTrimmed_When_GivenPaddedMixedCase()
{
UseCaseKey.From(" Kick-Uptime ").Value.Should().Be("kick-uptime");
}

[Fact]
public void Should_AcceptMaxLength_When_ExactlyAtLimit()
{
var atLimit = new string('a', UseCaseKey.MaxLength);

UseCaseKey.From(atLimit).Value.Should().Be(atLimit);
}

[Fact]
public void Should_BeEqual_When_SameNormalizedValue()
{
UseCaseKey.From("kick-uptime").Should().Be(UseCaseKey.From("KICK-UPTIME"));
}

[Fact]
public void Should_NotBeEqual_When_DifferentValue()
{
UseCaseKey.From("kick-uptime").Should().NotBe(UseCaseKey.From("dispel-debuff"));
}
}
Loading
Loading