Organisationsvorlagen: ein ganzes Haus als importierbare Datei (#691)
- Core/Organisation/OrgTemplate: format, export, parse, structural checks. Export empties every credential (nested, name variants) and names it; a template that carries one is refused. - OrgAdministration: ExportTemplate, PreviewTemplate (problems, warnings, every text in full), ImportTemplateAsync - only with confirmation, units before agents, own workspace per unit, doctrines, model and budget factor overridable, templateOrigin on every unit and agent. - Endpoints GET /organisation/template, POST .../preview, POST .../import; templates card in the organisation view. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 5.5
parent
11465f3d74
commit
706b5cfd9e
@@ -42,6 +42,16 @@ public sealed record InspectionInputDto(bool Enabled, double? Rate, double? Dens
|
||||
|
||||
public sealed record FindingDto(string Kind, string Text);
|
||||
|
||||
/// <summary>#691: A template to preview or import, with what the human changes on the way in.</summary>
|
||||
public sealed record TemplateImportDto(
|
||||
System.Text.Json.JsonElement Template,
|
||||
string? Model = null,
|
||||
Dictionary<string, string>? Models = null,
|
||||
decimal? BudgetFactor = null,
|
||||
bool Confirm = false,
|
||||
string Source = "",
|
||||
string Reason = "");
|
||||
|
||||
/// <summary>#702: What a unit puts to a blind vote, and who votes.</summary>
|
||||
public sealed record UnitVotesDto(IReadOnlyList<string> Operations, IReadOnlyList<string> Voters, int ApproveAt, int RejectAt, int Quorum, double Hours, int PerDay);
|
||||
|
||||
|
||||
@@ -105,6 +105,26 @@ internal static class OrganisationEndpoints
|
||||
return Results.Ok(new OutcomeDto(true, set.IsEmpty ? $"Doctrine of '{unit.Name}' removed." : $"Doctrine of '{unit.Name}' set (version {set.Version})."));
|
||||
});
|
||||
|
||||
// #691: The organisation as a template file — exported without credentials, imported only after a preview and a confirmation.
|
||||
org.MapGet("/template", (AppHost host, string? name, string? description) =>
|
||||
Results.Text(System.Text.Json.JsonSerializer.Serialize(
|
||||
host.Organisation.ExportTemplate(name ?? host.Instance.InstanceId, description ?? ""), Core.Organisation.OrgTemplates.Json),
|
||||
"application/json"));
|
||||
|
||||
org.MapPost("/template/preview", (AppHost host, TemplateImportDto input) =>
|
||||
{
|
||||
if (ReadTemplate(input) is not { } template)
|
||||
return Results.BadRequest(new OutcomeDto(false, TemplateError(input)));
|
||||
return Results.Ok(host.Organisation.PreviewTemplate(template, Overrides(input)));
|
||||
});
|
||||
|
||||
org.MapPost("/template/import", async (AppHost host, TemplateImportDto input, CancellationToken ct) =>
|
||||
{
|
||||
if (ReadTemplate(input) is not { } template)
|
||||
return Results.BadRequest(new OutcomeDto(false, TemplateError(input)));
|
||||
return (await host.Organisation.ImportTemplateAsync(template, Overrides(input), input.Confirm, input.Source, input.Reason, ct)).ToResponse();
|
||||
});
|
||||
|
||||
// #702: What the unit puts to a blind vote, and the votes held.
|
||||
org.MapPut("/units/{unitId}/votes", (AppHost host, string unitId, VotesInputDto input) =>
|
||||
host.Organisation.SetVotes(unitId, input.Operations, input.Voters, input.ApproveAt, input.RejectAt, input.Quorum, input.Hours, input.PerDay, input.Reason).ToResponse());
|
||||
@@ -212,4 +232,21 @@ internal static class OrganisationEndpoints
|
||||
|
||||
private static StructurePreviewDto ToDto(StructurePreview preview)
|
||||
=> new(preview.AffectedAgents, preview.ToolsLost, preview.Running, preview.Problems, preview.CanApply);
|
||||
// ─── templates (#691) ───
|
||||
|
||||
private static Core.Organisation.OrgTemplate? ReadTemplate(TemplateImportDto input)
|
||||
{
|
||||
try { return Core.Organisation.OrgTemplates.Parse(input.Template.GetRawText()); }
|
||||
catch (FormatException) { return null; }
|
||||
catch (InvalidOperationException) { return null; }
|
||||
}
|
||||
|
||||
private static string TemplateError(TemplateImportDto input)
|
||||
{
|
||||
try { Core.Organisation.OrgTemplates.Parse(input.Template.GetRawText()); return "The template could not be read."; }
|
||||
catch (Exception ex) { return ex.Message; }
|
||||
}
|
||||
|
||||
private static App.Services.TemplateOverrides Overrides(TemplateImportDto input)
|
||||
=> new(input.Model, input.Models, input.BudgetFactor);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user