Grossaufgaben und Sonderbudget (#717, Stufe 2+3)

Eine Aufgabe, deren eigene Schaetzung (gemessen, Geschwister oder Autor)
ueber der Haelfte der Tageskostengrenze liegt, bekommt zuerst einen
Planungslauf: teilen (Teile unter der Elternaufgabe) oder Sonderbudget
beantragen. Danach wartet sie auf ihre Teile bzw. die Freigabe und laeuft
einmal zum Zusammenfuehren.

Sonderbudget ueber Escalation.request_budget, ausnahmslos freigabepflichtig
(auch gegen Werkzeugkonfiguration und Dauerfreigabe). Laeufe im Baum ziehen
den Topf statt der Tagesgrenzen, tragen BudgetPot in Verbrauch und Beleg und
sind in der Kostenanalyse (nach Budget) und auf der Aufgabenkarte gesondert
sichtbar. Aufgebraucht zaehlen sie wieder gegen den Tag.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
Richard
2026-09-25 15:47:41 +02:00
co-authored by Claude Opus 5.5
parent 183e922278
commit 67649559f5
30 changed files with 1123 additions and 55 deletions
@@ -373,6 +373,15 @@ public sealed class SqliteStorage
DecidedAt TEXT NOT NULL,
Json TEXT NOT NULL
);
-- #717: Special budgets — one pot per task tree, approved by a human.
CREATE TABLE IF NOT EXISTS SpecialBudgets (
TaskId TEXT PRIMARY KEY COLLATE NOCASE,
AmountUsd TEXT NOT NULL,
Reason TEXT NOT NULL DEFAULT '',
RequestedBy TEXT NOT NULL DEFAULT '',
ApprovedAt TEXT NOT NULL
);
""";
cmd.ExecuteNonQuery();
@@ -418,6 +427,13 @@ public sealed class SqliteStorage
SpalteErgaenzen(connection, "Tasks", "EstimateUsd", "TEXT NULL");
SpalteErgaenzen(connection, "Tasks", "FillAlertedAt", "TEXT NULL");
// #717: A large task got its planning run — split or special budget, the agent proposed.
SpalteErgaenzen(connection, "Tasks", "LargePlannedAt", "TEXT NULL");
// #717: Runs that drew on a special budget — outside the daily limits, visible apart.
SpalteErgaenzen(connection, "RunUsage", "BudgetPot", "TEXT NOT NULL DEFAULT ''");
SpalteErgaenzen(connection, "RunReceipts", "BudgetPot", "TEXT NULL");
// Findet offene Aufrufe beim Start, ohne die ganze Tabelle zu lesen.
using var index = connection.CreateCommand();
index.CommandText = """