using ClawdDotNet.Core.Cases; using ClawdDotNet.Core.Config; using ClawdDotNet.Core.Organisation; using Shouldly; namespace ClawdDotNet.Core.Tests.Cases; /// #689: The rules of escalation, conflict and decision — read off the tree, none configured. public sealed class CaseRulesTests { /// /// purchasing (lead: buy-lead) › team-a (lead: a-lead; buyer-a), team-b (lead: b-lead; buyer-b); /// devops (lead: ops-lead; dev). No lead above the top units — the top is a human. /// private static OrgChart House() => new( [ new UnitConfig { Id = "purchasing", Name = "Purchasing", LeaderAgentId = "buy-lead" }, new UnitConfig { Id = "team-a", Name = "Team A", ParentId = "purchasing", LeaderAgentId = "a-lead" }, new UnitConfig { Id = "team-b", Name = "Team B", ParentId = "purchasing", LeaderAgentId = "b-lead" }, new UnitConfig { Id = "devops", Name = "DevOps", LeaderAgentId = "ops-lead" } ], [ new AgentConfig { AgentId = "buy-lead", UnitId = "purchasing" }, new AgentConfig { AgentId = "a-lead", UnitId = "team-a" }, new AgentConfig { AgentId = "buyer-a", UnitId = "team-a" }, new AgentConfig { AgentId = "b-lead", UnitId = "team-b" }, new AgentConfig { AgentId = "buyer-b", UnitId = "team-b" }, new AgentConfig { AgentId = "ops-lead", UnitId = "devops" }, new AgentConfig { AgentId = "dev", UnitId = "devops" } ]); [Fact] public void An_escalation_goes_to_the_own_lead_one_level_at_a_time() { var org = House(); CaseRules.NextHolder(org, "buyer-a").ShouldBe("a-lead"); CaseRules.NextHolder(org, "a-lead").ShouldBe("buy-lead"); CaseRules.NextHolder(org, "buy-lead").ShouldBe(CaseRules.Human, "above the top is a human"); CaseRules.NextHolder(null, "anyone").ShouldBe(CaseRules.Human); } [Fact] public void Two_teams_of_one_area_are_decided_by_that_area_s_lead_and_nobody_above_is_bothered() { var route = CaseRules.RouteConflict(House(), "a-lead", "b-lead"); route.Decider.ShouldBe("buy-lead"); route.Informed.ShouldBeEmpty(); } [Fact] public void Two_areas_go_to_the_top() { var route = CaseRules.RouteConflict(House(), "buy-lead", "ops-lead"); route.DecidedByHuman.ShouldBeTrue(); } [Fact] public void A_conflict_is_delivered_directly_and_the_levels_between_are_told_not_asked() { var route = CaseRules.RouteConflict(House(), "buyer-a", "dev"); route.DecidedByHuman.ShouldBeTrue("purchasing and devops meet only at the top"); route.Informed.ShouldBe(["a-lead", "buy-lead", "ops-lead"], ignoreOrder: true); } [Fact] public void Someone_above_the_other_side_decides_it_that_is_no_conflict() { CaseRules.RouteConflict(House(), "a-lead", "buyer-a").Refusal.ShouldNotBeNull().ShouldContain("no conflict"); CaseRules.RouteConflict(House(), "buyer-a", "buy-lead").Refusal.ShouldNotBeNull().ShouldContain("escalate"); } [Fact] public void A_decision_binds_the_deciding_lead_s_area_and_everything_below() { var org = House(); var decision = new Decision { Id = "d-1", ScopeUnitId = CaseRules.ScopeOf(org, "buy-lead") }; decision.ScopeUnitId.ShouldBe("purchasing"); CaseRules.Binds(org, decision, "buyer-a").ShouldBeTrue(); CaseRules.Binds(org, decision, "dev").ShouldBeFalse(); CaseRules.Binds(org, decision with { ScopeUnitId = CaseRules.ScopeOf(org, CaseRules.Human) }, "dev") .ShouldBeTrue("a human decides for the whole instance"); } [Fact] public void A_decision_is_found_by_its_subject_or_by_telling_words() { var decisions = new[] { new Decision { Id = "d-1", Subject = "Delivery date field in orders", Question = "Where do we record the delivery date?" }, new Decision { Id = "d-2", Subject = "Weekly report format" } }; CaseRules.Find(decisions, "delivery date field orders").Select(d => d.Id).ShouldBe(["d-1"]); CaseRules.Find(decisions, "Order confirmation", "The supplier confirmed a delivery date, where does it go?") .Select(d => d.Id).ShouldBe(["d-1"]); CaseRules.Find(decisions, "Holiday plan").ShouldBeEmpty(); } }