docs(563): state each binder test's mechanism instead of its mutant colour, per the amended CLAIMS rule

`testing.mutation-claims-are-executed` was amended on main while this branch was in review (#881,
merged as #914): a sentence asserting that a specific mutation reddens — or does not redden — a named
test is now either a `CLAIMS` entry in `scripts/tests/mutation_manifest.py` that executes every run,
or it is not written. This branch carried six such sentences and none of them can be declared:
`Claim.node_id` resolves a proof to `scripts/tests/<node id>` and `run_pytest` invokes pytest, so an
NUnit proof has no representation in that harness at all.

Durable prose now states the mechanism each test is built on — which serializer difference, which
engine branch — which a reader re-checks by reading the code rather than by trusting a remembered
outcome. The record says that in one paragraph, so the limit is stated rather than papered over.

The outcomes themselves are here. Re-measured 2026-09-05 on this branch's tree (the commit before
this one), each mutant applied to the working tree and restored from the index between runs, tree
verified clean afterwards:

  positive control          ScriptedScheduleControllerTests   Passed: 9, Failed: 0
                            OpenApiSerializerContractTests    Passed: 4, Failed: 0
  Bind<T> -> System.Text.Json with JsonSerializerDefaults.Web
                            Failed: 2, Passed: 7 — Production_Body_Binder_Ignores_Required_Members,
                            Production_Body_Binder_Keeps_Declared_Defaults_Over_An_Explicit_Null
  BodyBinderSettings = ApiJsonSettings.Create() -> new JsonSerializerSettings()
                            Failed: 1, Passed: 8 —
                            Production_Body_Binder_Keeps_Declared_Defaults_Over_An_Explicit_Null
  OpenApiSerializerContractTests RuntimeSettings -> new JsonSerializerSettings()
                            Failed: 4, Passed: 0 — all four cases, on PascalCase keys
  ScriptedScheduleController AddDuration(..., request.Trim, ...) -> false
                            Failed: 1, Passed: 8 — Committed_Script_Fixture_Produces_The_Pinned_Snapshot
  ScriptedScheduleController PadUntilExact(..., request.Trim, ...) -> false
                            Failed: 1, Passed: 8 — Committed_Script_Fixture_Produces_The_Pinned_Snapshot

The last one is the round-two finding closed and re-witnessed: before the fixture's pad target moved
off the content boundary, that mutant left all nine green.

A squash merge writes its own message, so these figures also belong in the PR description.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_015QqCpYFsKgnAnx6jVwrKiV
This commit is contained in:
2026-09-05 09:31:42 +02:00
co-authored by Claude Fable 5.1
parent d6e4426aa9
commit 3df98d247f
6 changed files with 45 additions and 31 deletions
@@ -68,7 +68,7 @@ public class ScriptedScheduleControllerTests
private static readonly DateTimeOffset Start = new(2026, 1, 15, 6, 0, 0, TimeSpan.Zero);
// What Startup hands to AddNewtonsoftJson, from the same function rather than a hand-copy of it.
// Replacing this with a plain `new()` reddens Production_Body_Binder_Keeps_Declared_Defaults_...
// The two Production_Body_Binder_* tests below are what hold this to the production configuration.
private static readonly JsonSerializerSettings BodyBinderSettings = ApiJsonSettings.Create();
// A body that omits the `required` member "collection"; Newtonsoft and System.Text.Json disagree.
@@ -161,9 +161,9 @@ public class ScriptedScheduleControllerTests
// The claim is about the SERIALIZER only — what MVC's validation layer then does with such a
// body (non-nullable reference types pick up an implicit required check) is the uncovered
// wrapper, not this. Binding through Bind itself is what gives this teeth: the fixture's own
// bodies parse identically under either serializer, so only a body like this one witnesses the
// swap. It witnesses THAT swap only — a Newtonsoft settings object that has merely lost the
// production configuration still binds this body correctly, which is the sibling test's job.
// bodies parse identically under either serializer, so only a body like this one separates them.
// It separates THAT pair only — a Newtonsoft settings object that has merely lost the production
// configuration reads this body exactly as production does, which is the sibling test's subject.
using JsonDocument document = JsonDocument.Parse(BodyMissingRequiredMember);
ContentCollection bound = Bind<ContentCollection>(document.RootElement).ShouldNotBeNull();
@@ -184,8 +184,8 @@ public class ScriptedScheduleControllerTests
// production sets NullValueHandling.Ignore, so an explicit `"order": null` leaves ContentCollection
// at its declared "shuffle" rather than overwriting it. Newtonsoft's own default is Include, which
// writes the null through — and AddCollection's Enum.TryParse then rejects it as a 400. So this is
// a behaviour difference a script would see, not a settings-shape assertion: it reddens if the bind
// settings are replaced by a plain JsonSerializerSettings that merely looks like the production one.
// a behaviour difference a script would see, not a settings-shape assertion — which is what makes
// the "shuffle" and the OkResult below assertions about the production configuration itself.
(ScriptedScheduleController controller, SchedulingEngine engine) = NewSession();
using JsonDocument document = JsonDocument.Parse(BodyWithExplicitNullOrder);