Fuellarbeit: zeitlich flexible Arbeit fuellt ungenutztes Budget (#717, Stufe 1)
Eine einmalige Aufgabe mit fill_by (spaetestens bis) statt Termin laeuft, wenn der Tag Budget uebrig laesst: nach regulaerer und nachgeholter Arbeit, nur wenn nichts vom Agenten wartet, bevorzugt ab 18 Uhr, und nur wenn ihre eigene Schaetzung in den Spielraum aus #716 passt. Kurz vor der Frist wird einmal ein Mensch gefragt; nach der Frist laeuft sie als regulaere Arbeit. Ehrliche Schaetzung (TaskCostEstimator): eigene Laeufe, dann Geschwister unter derselben Elternaufgabe, dann die Angabe des Autors (als Schaetzung gekennzeichnet), zuletzt das Mittel des Agenten. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
25f74d9ad6
commit
183e922278
@@ -21,7 +21,10 @@ public sealed record TaskInput(
|
||||
string? TimeZone = null,
|
||||
string Acceptance = "",
|
||||
int Priority = 3,
|
||||
string ParentId = "");
|
||||
string ParentId = "",
|
||||
// #717: fill work — at the latest by (ISO or yyyy-MM-dd), and the author's estimate per run.
|
||||
string? FillBy = null,
|
||||
decimal? EstimateUsd = null);
|
||||
|
||||
/// <summary>
|
||||
/// Das Taskboard fuer die Oberflaechen (#594): anlegen, aendern, pausieren, sofort ausfuehren,
|
||||
@@ -88,6 +91,8 @@ public sealed class TaskAdministration(
|
||||
return AdminResult.Failure(fehler);
|
||||
if (await CheckParentAsync(null, input.ParentId, ct) is { } parentProblem)
|
||||
return AdminResult.Failure(parentProblem);
|
||||
if (CheckFill(input, when, out var fillBy) is { } fillProblem)
|
||||
return AdminResult.Failure(fillProblem);
|
||||
|
||||
var created = await board.CreateAsync(new TaskItem
|
||||
{
|
||||
@@ -99,7 +104,9 @@ public sealed class TaskAdministration(
|
||||
When = when,
|
||||
Status = TaskItemStatus.Todo,
|
||||
Type = TaskItemType.Work,
|
||||
ParentId = input.ParentId.Trim()
|
||||
ParentId = input.ParentId.Trim(),
|
||||
FillBy = fillBy,
|
||||
EstimateUsd = input.EstimateUsd
|
||||
}, ct);
|
||||
|
||||
return AdminResult.Success(MitHinweis($"Aufgabe '{created.Title}' angelegt ({created.Id}).", created));
|
||||
@@ -114,6 +121,8 @@ public sealed class TaskAdministration(
|
||||
return AdminResult.Failure(fehler);
|
||||
if (await CheckParentAsync(id, input.ParentId, ct) is { } parentProblem)
|
||||
return AdminResult.Failure(parentProblem);
|
||||
if (CheckFill(input, when, out var fillBy) is { } fillProblem)
|
||||
return AdminResult.Failure(fillProblem);
|
||||
|
||||
var updated = await board.UpdateAsync(id, current => current with
|
||||
{
|
||||
@@ -123,7 +132,9 @@ public sealed class TaskAdministration(
|
||||
Priority = Math.Clamp(input.Priority, 1, 5),
|
||||
Assignee = assignee,
|
||||
When = when,
|
||||
ParentId = input.ParentId.Trim()
|
||||
ParentId = input.ParentId.Trim(),
|
||||
FillBy = fillBy,
|
||||
EstimateUsd = input.EstimateUsd
|
||||
}, ct);
|
||||
|
||||
return updated is null
|
||||
@@ -131,6 +142,27 @@ public sealed class TaskAdministration(
|
||||
: AdminResult.Success(MitHinweis($"Aufgabe '{updated.Title}' gespeichert.", updated));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// #717: Fill work is one-time, and its deadline lies ahead — a recurring schedule and a
|
||||
/// deadline contradict each other.
|
||||
/// </summary>
|
||||
private static string? CheckFill(TaskInput input, TaskWhen? when, out DateTime? fillBy)
|
||||
{
|
||||
fillBy = null;
|
||||
if (input.EstimateUsd is < 0)
|
||||
return "The estimate cannot be negative.";
|
||||
if (string.IsNullOrWhiteSpace(input.FillBy))
|
||||
return null;
|
||||
if (TaskFrontmatter.ParseUtc(input.FillBy) is not { } parsed)
|
||||
return $"'{input.FillBy}' is not a point in time. Use a date (yyyy-MM-dd) or an ISO timestamp.";
|
||||
if (when is { Kind: TaskWhenKind.Cron or TaskWhenKind.Every })
|
||||
return "Fill work is one-time: a deadline and a recurring schedule do not go together.";
|
||||
if (parsed <= DateTime.UtcNow)
|
||||
return "The deadline of fill work has to lie ahead.";
|
||||
fillBy = parsed;
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>#714: The parent must exist and must not lie below the task itself.</summary>
|
||||
private async Task<string?> CheckParentAsync(string? taskId, string parentId, CancellationToken ct)
|
||||
{
|
||||
|
||||
Reference in New Issue
Block a user