namespace IdleSpectator; /// /// Single source of truth: would the orange dossier show this EventLed Label? /// Selection must equal presentation - unpresentable beats must not win Watching. /// public static class EventPresentability { /// /// True when the candidate may own Layer A camera. /// Empty Label is only allowed for CharacterLed fill (task chip, no orange reason). /// public static bool WouldShow(InterestCandidate scene) { if (scene == null) { return false; } if (string.IsNullOrEmpty(scene.Label)) { return scene.LeadKind == InterestLeadKind.CharacterLed; } return !string.IsNullOrEmpty(UnitDossier.EvaluateSceneLabel(scene, recordDrops: false)); } /// /// Shown orange reason, or empty when blanked by identity / ownership rules. /// public static string ShownReason(InterestCandidate scene, bool recordDrops = true) { return UnitDossier.EvaluateSceneLabel(scene, recordDrops); } /// /// Harness synthetic tips ("HoldAction") must be subject-led EventReason shape /// so the presentability gate matches production dossier rules. /// public static string EnsureSubjectLedLabel(Actor subject, string label) { if (string.IsNullOrEmpty(label)) { return label ?? ""; } string name = EventFeedUtil.SafeName(subject); if (string.IsNullOrEmpty(name)) { return label; } if (label.StartsWith(name, System.StringComparison.OrdinalIgnoreCase)) { return label; } // "Name is in scene: HoldAction" → EventSentence + subject-led first token. return name + " is in scene: " + label; } }