Doctrine: die Leitung steuert ihre Einheit, ohne jeden Auftragstext anzufassen (#698)
- SqliteDoctrineStore: je Einheit versioniert (Tabelle Doctrines: Text, Autor, Lauf, Grund),
hoechstens 2000 Zeichen, leer entfernt; im Speicher gehalten fuer jeden Lauf
- OrgBriefing: die Doctrine der eigenen Einheit, nur eine Ebene tief, mit dem Satz,
dass ein Widerspruch zur Aufgabe eskaliert wird; Leitungen erfahren doctrine_set
- Escalation: doctrine_read, doctrine_set (nur die Leitung, Grund Pflicht)
- IAgentTool.Vorgabe(operation, context): Einstufung mit dem Aufrufkontext; eine Leitung
der Fremdinhalt-Stufe legt doctrine_set zur Freigabe vor
- API GET/PUT /organisation/units/{id}/doctrine; WebUI-Karte mit Verlauf und Markierung
haeufiger Aenderungen; i18n en/de
- Tests: DoctrineTests (5)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
c608af1abb
commit
679333b382
@@ -22,6 +22,11 @@ public sealed record UnitDto(
|
||||
// #687: what the lead may approve instead of the human; null: nothing.
|
||||
UnitApprovalsDto? Approvals = null);
|
||||
|
||||
/// <summary>#698: One version of a unit's doctrine.</summary>
|
||||
public sealed record DoctrineDto(int Version, string Text, string SetBy, DateTime SetAt, string? RunId, string Reason);
|
||||
|
||||
public sealed record DoctrineInputDto(string? Text, string? Reason);
|
||||
|
||||
public sealed record UnitApprovalsDto(IReadOnlyList<string> Operations, int PerDay, double Hours);
|
||||
|
||||
public sealed record ApprovalsInputDto(IReadOnlyList<string>? Operations, int? PerDay, double? Hours, string Reason = "");
|
||||
|
||||
@@ -81,6 +81,28 @@ internal static class OrganisationEndpoints
|
||||
? Results.Ok(ToDto(host.Organisation.PreviewMoveAgent(agentId, input.Target)))
|
||||
: host.Organisation.MoveAgent(agentId, input.Target, input.Reason, input.DropExcessTools).ToResponse());
|
||||
|
||||
// #698: A unit's doctrine — every version, and the human setting it.
|
||||
org.MapGet("/units/{unitId}/doctrine", async (AppHost host, string unitId, CancellationToken ct) =>
|
||||
{
|
||||
if (host.Doctrines is not { } store)
|
||||
return Results.Ok(Array.Empty<DoctrineDto>());
|
||||
return Results.Ok((await store.HistoryAsync(unitId, ct))
|
||||
.Select(d => new DoctrineDto(d.Version, d.Text, d.SetBy, d.SetAt, d.RunId, d.Reason)).ToList());
|
||||
});
|
||||
|
||||
org.MapPut("/units/{unitId}/doctrine", async (AppHost host, string unitId, DoctrineInputDto input, CancellationToken ct) =>
|
||||
{
|
||||
if (host.Doctrines is not { } store)
|
||||
return Results.BadRequest(new OutcomeDto(false, "Doctrines are not available in this instance."));
|
||||
if (host.Organisation.Current().Unit(unitId) is not { } unit)
|
||||
return Results.BadRequest(new OutcomeDto(false, $"There is no unit '{unitId}'."));
|
||||
var text = (input.Text ?? "").Trim();
|
||||
if (text.Length > Core.Organisation.SqliteDoctrineStore.MaxLength)
|
||||
return Results.BadRequest(new OutcomeDto(false, $"A doctrine has at most {Core.Organisation.SqliteDoctrineStore.MaxLength} characters."));
|
||||
var set = await store.SetAsync(unit.Id, text, "human", null, input.Reason ?? "", ct);
|
||||
return Results.Ok(new OutcomeDto(true, set.IsEmpty ? $"Doctrine of '{unit.Name}' removed." : $"Doctrine of '{unit.Name}' set (version {set.Version})."));
|
||||
});
|
||||
|
||||
// #687: What the lead may approve instead of the human.
|
||||
org.MapPut("/units/{unitId}/approvals", (AppHost host, string unitId, ApprovalsInputDto input) =>
|
||||
host.Organisation.SetApprovals(unitId, input.Operations, input.PerDay, input.Hours, input.Reason).ToResponse());
|
||||
|
||||
Reference in New Issue
Block a user