using ClawdDotNet.Core.Config;
using ClawdDotNet.Core.Organisation;
namespace ClawdDotNet.Core.Cases;
///
/// #690: The deciding instance at the top of the tree (Organisation-Konzept, section 6).
///
/// A role, not a person. A human, an agent, or the human first with the agent
/// behind — otherwise the kit would fix one picture of a company.
///
/// What a deciding agent may not do matters more than what it may. It decides
/// questions and does not carry them out; it has no tool with an effect outside; it does not
/// change the structure. Otherwise the top of the organisation is its largest hole — a model
/// with full power and nobody opposite. So it holds only the tools of
/// and stands outside the units; an agent that does not fit is not given a case — the human is.
///
/// It makes the organisation faster, not more right. Its data comes from the same
/// instance, with the same possible errors. Every decision it takes is therefore presented to
/// the human to read (), and the deadline holds for it too.
///
public static class TopRole
{
///
/// What a deciding agent may hold: tasks for the units that carry a decision out, its own
/// case tool, memory, files inside the instance, and talking to agents. Nothing with an
/// effect outside (Mail, Telegram, social media, FTP, APIs), nothing that reads foreign
/// content into the top, no database, nothing that creates or edits agents.
///
public static readonly IReadOnlySet AllowedTools = new HashSet(StringComparer.OrdinalIgnoreCase)
{
"Taskboard", CaseDesk.ToolName, "Memory", "FileRW", "AgentComm"
};
///
/// Why this agent cannot take the top, or null. Checked when it is set and again
/// every time a case would go to it — the configuration may have changed in between.
///
public static string? Unfit(AgentConfig? agent, string agentId)
{
if (agent is null)
return $"There is no agent '{agentId}'.";
if (!string.IsNullOrWhiteSpace(agent.UnitId))
return $"'{agent.AgentId}' belongs to a unit. The deciding agent stands above all units, not in one — " +
"move it to the top level first.";
if (agent.TreatAsForeignReader)
return $"'{agent.AgentId}' is set to read content from outside. Whoever decides for the whole instance must not.";
var beyond = agent.Tools.Keys.Where(t => !AllowedTools.Contains(t)).OrderBy(t => t, StringComparer.OrdinalIgnoreCase).ToList();
if (beyond.Count > 0)
return $"'{agent.AgentId}' holds tools a deciding agent may not have: {string.Join(", ", beyond)}. " +
$"It decides, it does not carry out — allowed are {string.Join(", ", AllowedTools.Order(StringComparer.OrdinalIgnoreCase))}.";
return null;
}
/// The deciding agent, if the settings name one and it fits; otherwise null and why.
public static (AgentConfig? Agent, string? Problem) Resolve(TopSettings top, OrgChart? org)
{
if (!top.UsesAgent)
return (null, null);
var agent = org?.Agent(top.AgentId!.Trim());
var problem = Unfit(agent, top.AgentId!.Trim());
return problem is null ? (agent, null) : (null, problem);
}
}