using System.Text.Json; using ClawdDotNet.Core.Audit; using ClawdDotNet.Core.Config; using ClawdDotNet.Core.Inspection; using ClawdDotNet.Core.Organisation; using ClawdDotNet.Core.Storage; using ClawdDotNet.Core.Tasks; 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; /// #693: The lead's side of the spot checks through the Escalation tool. public sealed class SpotCheckToolTests : IDisposable { private readonly string _directory = Path.Combine(Path.GetTempPath(), "clawd-tests", Guid.NewGuid().ToString("N")); private readonly SqliteStorage _storage; private readonly SqliteAuditRepository _audit; private readonly SpotChecks _checks; private readonly EscalationTool _tool = new(); private readonly OrgChart _org = new( [ new UnitConfig { Id = "purchasing", Name = "Purchasing", LeaderAgentId = "buy-lead", Inspection = new UnitInspection { Rate = 1, DenseRate = 1, PerDay = 5 } }, new UnitConfig { Id = "sales", Name = "Sales", LeaderAgentId = "sales-lead" } ], [ new AgentConfig { AgentId = "buy-lead", UnitId = "purchasing" }, new AgentConfig { AgentId = "buyer", UnitId = "purchasing" }, new AgentConfig { AgentId = "sales-lead", UnitId = "sales" }, new AgentConfig { AgentId = "seller", UnitId = "sales" } ]); public SpotCheckToolTests() { _storage = new SqliteStorage(Path.Combine(_directory, "state.db")); _audit = new SqliteAuditRepository(_storage); var tasks = new SqliteTaskRepository(_storage); _checks = new SpotChecks(_storage, _audit, new RunInspector(_audit, tasks, null, () => RunInspector.VocabularyOf([_tool])), () => _org, new TaskboardService(tasks, Path.Combine(_directory, "tasks")), NullLogger.Instance); } public void Dispose() { SqliteStorage.ReleasePooledConnections(_directory); try { Directory.Delete(_directory, recursive: true); } catch { /* cleanup is secondary */ } } private Task Run(string agent, object input) => _tool.ExecuteAsync(JsonSerializer.SerializeToElement(input), new AgentToolContext(agent, "inst", new Dictionary(), new NullStateStore(), NullLogger.Instance, CancellationToken.None) { Organisation = _org, SpotChecks = _checks }, CancellationToken.None); private async Task RecordAsync(string runId, string agent, string said) { await _audit.AppendAsync(new AuditEntry { RunId = runId, AgentId = agent, Tool = "FileRW", Arguments = "{\"action\":\"append\"}", Status = AuditStatus.Ok, OccurredAt = DateTime.UtcNow.AddMinutes(-2) }, default); await _audit.RecordReceiptAsync(new RunReceipt { RunId = runId, AgentId = agent, Model = "m", Status = "Completed", StepCount = 2, ResultRef = said, OccurredAt = DateTime.UtcNow.AddMinutes(-1) }, default); } [Fact] public async Task The_lead_sees_its_checks_with_the_audit_findings_and_closes_them() { await RecordAsync("run-b", "buyer", "Escalation doctrine_set was refused."); var check = (await _checks.SampleAsync(default)).Single(); var list = await Run("buy-lead", new { action = "spot_checks" }); list.Content.ShouldContain($"check {check.Id}"); list.Content.ShouldContain("doctrine_set", customMessage: "the claim without a call is shown from the audit"); var detail = await Run("buy-lead", new { action = "inspect_run", run = "run-b" }); detail.Content.ShouldContain("FileRW.append: Ok"); detail.Content.ShouldContain("its claim, not evidence"); (await Run("buy-lead", new { action = "check_done", check = check.Id, verdict = "finding", note = "Reports a refusal that never happened." })) .Success.ShouldBeTrue(); (await Run("buy-lead", new { action = "spot_checks" })).Content.ShouldContain("No spot check waits for you"); } [Fact] public async Task A_lead_sees_no_runs_of_agents_it_does_not_lead() { await RecordAsync("run-s", "seller", "Done."); (await Run("buy-lead", new { action = "inspect_run", run = "run-s" })).ErrorMessage!.ShouldContain("do not lead"); (await Run("buyer", new { action = "inspect_run", run = "run-s" })).Success.ShouldBeFalse(); } [Fact] public async Task A_measure_needs_a_known_kind_and_goes_to_a_human() { (await Run("buy-lead", new { action = "propose_measure", agent = "buyer", measure = "fire", reasoning = "x" })) .ErrorMessage!.ShouldContain("'measure'"); var proposed = await Run("buy-lead", new { action = "propose_measure", agent = "buyer", measure = "change_model", reasoning = "Fails every table task." }); proposed.Success.ShouldBeTrue(proposed.ErrorMessage); proposed.Content.ShouldContain("with a human"); } }