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");
}
}