Automatisierungsvorschlaege nur mit n8n-Anbindung (#726)

An instance without n8n has no use for workflow proposals. New instance
setting automation.enabled ("this instance works with n8n"), off by default:
- off: automation_find/automation_propose refuse, no candidates are put to
  agents, and the escalation tool does not mention proposals at all;
- the switch sits in the settings card for external MCP servers; the
  approvals view says proposals are off instead of standing empty.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-10-01 20:29:19 +02:00
co-authored by Claude Opus 5.5
parent 184a507e3d
commit a17ac2957f
12 changed files with 124 additions and 4 deletions
@@ -9,6 +9,16 @@ using Microsoft.Extensions.Logging;
namespace ClawdDotNet.Core.Automation;
/// <summary>
/// #726: Whether the instance works with n8n. Off by default: an instance without n8n has no
/// use for workflow proposals, and every proposal and candidate costs runs and attention.
/// </summary>
public sealed class AutomationSettings
{
[System.Text.Json.Serialization.JsonPropertyName("enabled")]
public bool Enabled { get; set; }
}
/// <summary>What the replaced tasks cost over the measured days — taken from the receipts, not estimated.</summary>
public sealed record MeasuredSavings(int Days, int Runs, decimal CostUsd, long Tokens)
{
@@ -32,6 +42,9 @@ public sealed record AutomationOutcome(bool Ok, string Message, long? Id = null)
public interface IAutomationProposals
{
/// <summary>Whether the instance works with n8n — without it, nothing is proposed.</summary>
bool Enabled { get; }
Task<IReadOnlyList<AutomationProposal>> FindAsync(string subject, CancellationToken ct);
Task<AutomationOutcome> ProposeAsync(AutomationRequest request, CancellationToken ct);
@@ -57,8 +70,13 @@ public sealed class AutomationProposals(
ITaskRepository tasks,
TaskboardService? board,
ILogger logger,
Func<AutomationSettings>? settings = null,
TimeProvider? clock = null) : IAutomationProposals, IAsyncDisposable
{
public bool Enabled => settings?.Invoke().Enabled ?? true;
public const string DisabledMessage = "This instance does not work with n8n — workflow proposals are switched off.";
public const int MaxOpenPerAgent = 3;
public const int MeasureDays = 28;
@@ -85,6 +103,8 @@ public sealed class AutomationProposals(
public async Task<AutomationOutcome> ProposeAsync(AutomationRequest r, CancellationToken ct)
{
if (!Enabled)
return new(false, DisabledMessage);
if (string.IsNullOrWhiteSpace(r.Purpose) || string.IsNullOrWhiteSpace(r.Trigger))
return new(false, "'purpose' and 'trigger' are required — what it is for, and what starts it.");
if (r.TaskIds.Count == 0)
@@ -236,7 +256,7 @@ public sealed class AutomationProposals(
/// </summary>
public async Task<IReadOnlyList<string>> FindCandidatesAsync(CancellationToken ct)
{
if (board is null)
if (board is null || !Enabled)
return [];
var since = Now.AddDays(-CandidateDays);
var proposals = await ListAsync(null, 1000, ct);
@@ -159,6 +159,10 @@ public sealed class InstanceConfig
[JsonPropertyName("fileHistory")]
public Files.FileHistorySettings FileHistory { get; set; } = new();
/// <summary>#726: Whether this instance works with n8n — only then do agents propose workflows. Off by default.</summary>
[JsonPropertyName("automation")]
public Automation.AutomationSettings Automation { get; set; } = new();
/// <summary>#197: External MCP servers whose tools the agents may use — set up by a human only.</summary>
[JsonPropertyName("mcpServers")]
public List<Mcp.McpServerConfig> McpServers { get; set; } = new();