- TaskSchedule.OccurrencesBetween zaehlt, wie oft eine Aufgabe bis zu einem Zeitpunkt noch laeuft, einschliesslich eines jetzt faelligen Laufs. - BudgetHeadroom (ILateRunGate): Ein mitgenommener oder nachzuholender Lauf startet nur, wenn er in den Spielraum passt: Grenze minus Verbrauch minus Reservierung minus Bedarf der restlichen geplanten Laeufe bis Mitternacht minus 10 % Reserve. Das gilt je Agent und je Instanz, fuer Kosten und Tokens. Anderes Spaetwerk zaehlt nicht als Bedarf, sonst blockierten sich mitgenommene Aufgaben gegenseitig. - Der Scanner fragt den Spielraum nur fuer Spaetwerk und protokolliert, wenn es warten muss. Die Engine gibt ihren BudgetGuard heraus, damit Scanner und Engine dieselben Reservierungen sehen. - Ersetzt den pauschalen 25-%-Anteil aus dem Konzept (dort nachgezogen): Ein fester Anteil ist an vollen Tagen zu viel, an ruhigen zu wenig. Drei weitere stille Kappungen bei 500 beseitigt (ListAllAsync, ungekappt): Kostenprognose (Aufgaben ueber 500 fehlten), Waisenabgleich beim Import und task_find aus #674, wo Dubletten unter aelteren Aufgaben mit niedriger Prioritaet unsichtbar wurden. Tests: BudgetHeadroomTests (9), Scanner-Pforte (1), Dublette hinter 500 Aufgaben (1). 1.257 gruen. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
113 lines
5.5 KiB
C#
113 lines
5.5 KiB
C#
namespace ClawdDotNet.Core.Tasks;
|
|
|
|
/// <summary>
|
|
/// Der Ausführungszustand des Taskboards. Die Definition der Aufgaben lebt in
|
|
/// Markdown-Dateien; dieses Repository ist die DB-Seite, die das Claiming atomar macht
|
|
/// (was ein Dateisystem nicht verlässlich kann) und dem Scanner erlaubt, ohne
|
|
/// Dateizugriff zu entscheiden.
|
|
/// </summary>
|
|
public interface ITaskRepository
|
|
{
|
|
/// <summary>
|
|
/// Legt eine Aufgabe an oder aktualisiert ihre Definition (Importer). Idempotent über
|
|
/// die <see cref="TaskItem.Id"/> — ein zweiter Import erzeugt keine Dublette. Der
|
|
/// Ausführungszustand (Marker, Claim) bleibt dabei unangetastet.
|
|
/// </summary>
|
|
Task<TaskItem> UpsertAsync(TaskItem task, CancellationToken ct);
|
|
|
|
Task<TaskItem?> GetAsync(string id, CancellationToken ct);
|
|
|
|
Task<IReadOnlyList<TaskItem>> ListAsync(TaskQuery query, CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// #682: Every task in status <c>todo</c> — the scanner's candidates, without a cap.
|
|
///
|
|
/// <para><see cref="ListAsync"/> clamps to 500 because it serves views and tools. The
|
|
/// scanner used it too, so beyond 500 tasks schedules silently fell out of the tick.
|
|
/// Only <c>todo</c> can be claimed, so this list is bounded by the work that is
|
|
/// actually waiting, not by the size of the board.</para>
|
|
/// </summary>
|
|
Task<IReadOnlyList<TaskItem>> ListRunnableAsync(CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// Every task, without a cap — for the few callers that must see the whole board: the
|
|
/// cost forecast, duplicate detection, the import's orphan sweep. Before #716 they used
|
|
/// <see cref="ListAsync"/> with its limit of 500 and silently saw only part of it.
|
|
/// </summary>
|
|
Task<IReadOnlyList<TaskItem>> ListAllAsync(bool includeArchived, CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// Beansprucht einen fälligen Termin atomar. Genau ein gleichzeitiger Aufruf gewinnt;
|
|
/// jeder weitere erhält <c>false</c>. Setzt den Marker (<paramref name="occurrenceKey"/>)
|
|
/// sofort — ein Termin ist damit auch dann verbraucht, wenn der Lauf später scheitert
|
|
/// oder der Prozess abstürzt (at-most-once, kein Retry-Sturm).
|
|
/// </summary>
|
|
/// <param name="occurrenceKey">Sortierbarer ISO-UTC-Zeitstempel des Termins.</param>
|
|
/// <param name="leaseCutoff">
|
|
/// Claims, die älter sind, gelten als verwaist (abgestürzter Lauf) und dürfen
|
|
/// überschrieben werden.
|
|
/// </param>
|
|
Task<bool> TryClaimAsync(
|
|
string id, string occurrenceKey, string claimToken,
|
|
DateTime now, DateTime leaseCutoff, CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// Schließt einen beanspruchten Lauf ab und setzt den Endstatus. Nur wirksam, solange
|
|
/// der Aufrufer den Claim noch hält (<paramref name="claimToken"/> passt) — ein Lauf,
|
|
/// der seine Lease verloren hat, überschreibt nichts mehr. Gibt zurück, ob der Claim
|
|
/// noch gehörte.
|
|
/// </summary>
|
|
Task<bool> CompleteClaimAsync(
|
|
string id, string claimToken, TaskItemStatus finalStatus,
|
|
DateTime now, CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// Gibt einen Claim zurück, als hätte es ihn nicht gegeben: Status <c>todo</c> und der
|
|
/// Marker auf <paramref name="previousOccurrence"/> (#643). Für Läufe, die gar nicht
|
|
/// stattfanden. Nur wirksam, solange der Aufrufer den Claim hält.
|
|
/// </summary>
|
|
Task<bool> ReleaseClaimAsync(
|
|
string id, string claimToken, string? previousOccurrence, DateTime now, CancellationToken ct);
|
|
|
|
/// <summary>Ändert den Status direkt (Tool-Aktionen, Auto-Dispatch, Eskalation).</summary>
|
|
Task<bool> SetStatusAsync(string id, TaskItemStatus status, DateTime now, CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// #716: The budget refused a run of a one-time task. Releases the claim, restores the
|
|
/// occurrence (the task is still due) and defers it to <paramref name="deferredUntil"/>.
|
|
/// </summary>
|
|
Task<bool> CarryOverAsync(
|
|
string id, string claimToken, string? previousOccurrence, DateTime deferredUntil,
|
|
DateTime now, CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// #716: The budget refused a run of a recurring task. Releases the claim back to
|
|
/// <c>todo</c>; the occurrence stays consumed. With <paramref name="catchUpUntil"/> a
|
|
/// single catch-up run is scheduled for then — an already pending one is kept.
|
|
/// </summary>
|
|
Task<bool> RecordBudgetMissAsync(
|
|
string id, string claimToken, DateTime? catchUpUntil, DateTime now, CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// #716: A run took place — clears the miss counter, a pending catch-up and the
|
|
/// deferral. <see cref="TaskItem.LastBudgetMissAt"/> stays as history.
|
|
/// </summary>
|
|
Task<bool> ClearBudgetMissAsync(string id, DateTime now, CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// Reconciliation beim Start: verwaiste Claims (älter als <paramref name="leaseCutoff"/>)
|
|
/// lösen und die betroffenen Aufgaben von <see cref="TaskItemStatus.InProgress"/> zurück
|
|
/// auf <see cref="TaskItemStatus.Todo"/> stellen. Gibt die Zahl der zurückgesetzten
|
|
/// Aufgaben zurück.
|
|
/// </summary>
|
|
Task<int> ReleaseStaleClaimsAsync(DateTime leaseCutoff, DateTime now, CancellationToken ct);
|
|
|
|
/// <summary>
|
|
/// Aufgaben, die auf <paramref name="blockerId"/> warten — Grundlage für den
|
|
/// Auto-Dispatch, wenn der letzte Blocker fertig wird.
|
|
/// </summary>
|
|
Task<IReadOnlyList<TaskItem>> ListBlockedByAsync(string blockerId, CancellationToken ct);
|
|
|
|
Task<int> CountAsync(CancellationToken ct);
|
|
}
|