Files
ersatztv/ErsatzTV.Core/Domain/Collection/MultiCollectionItemWeight.cs
T
caaae4cd00 docs(70): re-derive the stale-claim fix by grep instead of working the review's list
Round-3 review returned BLOCKED: must-fix (b) was not closed. It was right, and
the root cause it named is the point of this commit — the previous correction
"was scoped to the four sites the reviewer listed rather than re-derived by grep".
Fixing the list is not fixing the class. That is the same failure as B1, where the
gate covered the two writers already in hand and missed CreateChannelFromLineup.

Re-grepped the behavior class instead. Three survivors, two of them missed and one
freshly introduced by the correction itself:

- CreateMultiCollectionHandler.cs — the create twin of a comment whose UPDATE twin
  I corrected and whose create twin I never opened. Present tense, and contradicted
  by two tests in this same PR.
- decisions.md — corrected one line in that file and left its sibling.
- MultiCollectionItemWeight.cs (and its decisions.md mirror) — the ceiling rationale
  still claimed unbounded weights overflow the sum. They cannot: EffectiveWeight
  clamps before every sum and CycleLength widens to long. The earlier pass
  pattern-matched on the word "filtered" and left the identical defect on the
  ceiling. The ceiling's real job is the floor's argument — a billion is not a share
  of airtime any more than 0 is — so it now says that, and credits the clamp with the
  arithmetic safety it actually provides.

Also corrected the writer claim to the right predicate: not "two persisting writers"
(Add*ToPlaylist and Trakt persist it too, hardcoded) but two writers that persist a
CALLER-SUPPLIED order. The full set is now classified persists-caller-value /
persists-hardcoded / in-memory, including Engine/PlaylistHelper, which the previous
"two Preview handlers" phrasing missed. That bullet has been wrong three times in
the same shape; it now says so, since a lesson that keeps being re-learned is worth
recording as a pattern rather than a fact.

The BOM check caught this commit re-adding a BOM to the one file patched with
utf-8-sig — the same trap, an hour after writing it down. Stripped; the mechanical
pre-push check is what makes that survivable.

Core.Tests 566, ErsatzTV.Tests 1673, 0 failed. Format verify exit 0. decisions.md
+90/-0 (append-only guard green).

Refs #70

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-17 17:00:46 +00:00

33 lines
1.8 KiB
C#

namespace ErsatzTV.Core.Domain;
/// <summary>
/// Bounds for a multi-collection member's per-source <c>Weight</c> (#70).
/// <para>
/// The floor is <b>1, not 0</b>. A weight is a <i>share of airtime</i>, and 0 has no meaning on that scale —
/// "don't play this source" is expressed by removing it from the multi collection, not by weighting it to
/// nothing. Rejecting it here keeps that intent from being expressed in a way the rotation would have to
/// interpret. <c>WeightedShuffleCollectionEnumerator</c> independently clamps to this range rather than
/// trusting it, so a row that predates this gate rotates at the floor instead of vanishing; the two are
/// belt-and-braces, not duplicates — <b>neither is redundant</b>.
/// </para>
/// <para>
/// The ceiling is the same argument as the floor, not an arithmetic guard. A weight of a billion is not a
/// share of airtime any more than 0 is, so it is refused at the boundary rather than silently reinterpreted.
/// The rotation arithmetic is made safe by <c>EffectiveWeight</c>'s clamp (and by <c>CycleLength</c> summing
/// to <c>long</c>) — <b>not</b> by this constant: with the clamp in place a sum cannot overflow whatever the
/// stored value is, which is exactly why a row that predates this gate is still safe.
/// <c>An_Enormous_Weight_Does_Not_Overflow_The_Rotation</c> pins that.
/// </para>
/// </summary>
public static class MultiCollectionItemWeight
{
public const int Minimum = 1;
public const int Maximum = 1000;
public const int Default = 1;
public static bool IsValid(int weight) => weight is >= Minimum and <= Maximum;
public static string ValidationMessage =>
$"Weight must be between {Minimum} and {Maximum}";
}