using System.Text.Json; using ClawdDotNet.Core.Config; using ClawdDotNet.Core.Organisation; using ClawdDotNet.Core.Staging; using ClawdDotNet.Core.Storage; using ClawdDotNet.Core.Tools; using ClawdDotNet.Tools.Taskboard; using ClawdDotNet.Tools.Tests.FileRW; using Microsoft.Extensions.Logging.Abstractions; using Shouldly; namespace ClawdDotNet.Tools.Tests.Taskboard; /// /// #698: The lead steers its unit without touching every task text — one level deep, short, /// every version kept, and not unasked from a lead that reads foreign content. /// public sealed class DoctrineTests : IDisposable { private readonly string _directory = Path.Combine(Path.GetTempPath(), "clawd-tests", Guid.NewGuid().ToString("N")); private readonly SqliteDoctrineStore _store; private readonly EscalationTool _tool = new(); /// purchasing (buy-lead; buyer) › team (team-lead; clerk). private readonly OrgChart _org = new( [ new UnitConfig { Id = "purchasing", Name = "Purchasing", LeaderAgentId = "buy-lead" }, new UnitConfig { Id = "team", Name = "Team", ParentId = "purchasing", LeaderAgentId = "team-lead" } ], [ new AgentConfig { AgentId = "buy-lead", UnitId = "purchasing" }, new AgentConfig { AgentId = "buyer", UnitId = "purchasing" }, new AgentConfig { AgentId = "team-lead", UnitId = "team" }, new AgentConfig { AgentId = "clerk", UnitId = "team" } ]); public DoctrineTests() => _store = new SqliteDoctrineStore(new SqliteStorage(Path.Combine(_directory, "state.db"))); public void Dispose() { SqliteStorage.ReleasePooledConnections(_directory); try { Directory.Delete(_directory, recursive: true); } catch { /* cleanup is secondary */ } } private AgentToolContext Context(string agent, AgentTrust? trust = null) => new(agent, "inst", new Dictionary(), new NullStateStore(), NullLogger.Instance, CancellationToken.None) { Organisation = _org, Doctrines = _store, RunId = "run-" + agent, Trust = trust ?? AgentTrust.Standard }; private Task Run(string agent, object input) => _tool.ExecuteAsync(JsonSerializer.SerializeToElement(input), Context(agent), CancellationToken.None); [Fact] public async Task The_lead_sets_the_doctrine_and_its_members_read_it() { var set = await Run("buy-lead", new { action = "doctrine_set", text = "Every supplier contact goes into the database first.", reason = "Contacts got lost." }); set.Success.ShouldBeTrue(set.ErrorMessage); (await Run("buyer", new { action = "doctrine_read" })).Content.ShouldContain("supplier contact"); _store.Current("purchasing")!.SetBy.ShouldBe("buy-lead"); } [Fact] public async Task Only_a_lead_sets_it_with_a_reason_and_within_the_length() { (await Run("buyer", new { action = "doctrine_set", text = "x", reason = "y" })).ErrorMessage!.ShouldContain("Only the lead"); (await Run("buy-lead", new { action = "doctrine_set", text = "x" })).ErrorMessage!.ShouldContain("'reason'"); (await Run("buy-lead", new { action = "doctrine_set", text = new string('x', 2001), reason = "y" })).ErrorMessage!.ShouldContain("2000"); } [Fact] public async Task It_reaches_one_level_deep_and_no_further() { await _store.SetAsync("purchasing", "Purchasing rule.", "buy-lead", null, "r", default); await _store.SetAsync("team", "Team rule.", "team-lead", null, "r", default); var buyer = OrgBriefing.For(_org, "buyer", DateTime.UtcNow, doctrine: _store.Current)!; var clerk = OrgBriefing.For(_org, "clerk", DateTime.UtcNow, doctrine: _store.Current)!; buyer.ShouldContain("Purchasing rule."); clerk.ShouldContain("Team rule."); clerk.ShouldNotContain("Purchasing rule.", customMessage: "the team has its own lead and its own doctrine"); } [Fact] public async Task Every_version_is_kept_and_an_empty_text_removes_it() { await Run("buy-lead", new { action = "doctrine_set", text = "First.", reason = "a" }); await Run("buy-lead", new { action = "doctrine_set", text = "", reason = "no longer needed" }); _store.Current("purchasing").ShouldBeNull(); var history = await _store.HistoryAsync("purchasing", default); history.Select(d => d.Version).ShouldBe([2, 1]); history[1].Text.ShouldBe("First."); history[0].RunId.ShouldBe("run-buy-lead"); } [Fact] public void A_lead_that_reads_foreign_content_needs_an_approval_to_set_it() { _tool.Vorgabe("doctrine_set", Context("buy-lead", new AgentTrust(TrustTier.ReadsForeignContent, ["Mail"]))) .ShouldBe(StagingDecision.Approve); _tool.Vorgabe("doctrine_set", Context("buy-lead")).ShouldBeNull("a lead of the standard tier sets it without asking"); _tool.Vorgabe("doctrine_read", Context("buy-lead", new AgentTrust(TrustTier.ReadsForeignContent, ["Mail"]))).ShouldBeNull(); } }