← All source files

V14 / fad4e1a110e5

ProjectEditingOperations.cs

Lilith/Core/Projects/ProjectEditingOperations.cs

Unchanged from the preceding version
CS
namespace Lilith.Core.Projects;

internal static class ProjectEditingOperations
{
    internal static readonly string[] Names = ["project_edit_prepare_operation", "project_restore_prepare_operation", "project_backups_operation", "project_draft_read_operation", "project_draft_source_operation", "project_edit_require_operation", "project_tests_require_operation", "project_edit_apply_operation", "project_set_tests_operation", "project_run_tests_operation"];
    internal static async Task<string> ExecuteAsync(ProviderClient provider, string name, IReadOnlyDictionary<string, string> inputs, CancellationToken ct)
    {
        var store = new ProjectDraftStore(provider.Workspace ?? throw new InvalidOperationException("Project editing needs a workspace."));
        string Get(string key) => inputs.GetValueOrDefault(key, "");
        string id = Get("id");
        if (name == "project_draft_source_operation") return ProjectJson.Serialize(new { source = ProjectIntegrationCodec.Display(new(store.Load(id).Files)) });
        string Receipt(ProjectDraft draft) => ProjectJson.Serialize(new { planned = true, status = "awaiting_test_approval", draft.Id, draft.Name, tests = draft.Tests.Select(t => new { t.Id, t.Requirement }), message = "Private candidate and tests prepared. No generated application was run or published. Review with /project-review " + draft.Id + "; explicitly approve this project and up to ten test/repair rounds with /project-test " + draft.Id + ". Publication approval remains separate." });
        if (name == "project_tests_require_operation")
        {
            var draft = store.Load(id);
            if (draft.Tests.Length == 0 || draft.State != "awaiting_test_approval") throw new InvalidOperationException("No valid frozen test plan was prepared after three attempts. Nothing executed or published.");
            return Receipt(draft);
        }
        if (name == "project_edit_require_operation" && store.Load(id).Revision == 0) throw new InvalidOperationException("No valid source edit was prepared after three attempts. Private diagnostics are retained.");
        if (name == "project_backups_operation") return ProjectJson.Serialize(new { backups = store.Backups(Get("name")) });
        if (name == "project_restore_prepare_operation") return ProjectJson.Serialize(store.Inspect(store.Restore(Get("name"), Get("backup_id")).Id));
        if (name == "project_run_tests_operation")
        {
            if (provider.ProjectExecutionId != id) throw new InvalidOperationException("Explicit host execution approval required: /project-test <draft-id>. Models cannot approve execution.");
            return await ProjectQualification.RunAsync(provider, store, id, ct);
        }
        if (name == "project_edit_prepare_operation")
        {
            var draft = store.Import(Get("name"), Get("request")); return ProjectJson.Serialize(store.Inspect(draft.Id));
        }
        if (name is "project_edit_apply_operation" or "project_set_tests_operation")
        {
            // A separate host cannot change a draft while its qualification lease is held.
            using var lease = new FileStream(Path.Combine(store.Folder(id), "execution.lock"), FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None);
            if (name == "project_edit_apply_operation")
            {
                try { store.Apply(id, Get("changes")); return ProjectJson.Serialize(new { applied = true, id }); }
                catch (Exception ex) when (ex is InvalidDataException or System.Text.Json.JsonException)
                {
                    var draft = store.Load(id); store.Save(draft with { SourceReview = "Source edit was rejected: " + ex.Message });
                    return ProjectJson.Serialize(new { applied = false, id, diagnostics = ex.Message });
                }
            }
            else
            {
                try { return Receipt(store.AddTests(id, Get("plan"))); }
                catch (Exception ex) when (ex is InvalidDataException or System.Text.Json.JsonException)
                {
                    var draft = store.Load(id); store.Save(draft with { SourceReview = "Test plan was rejected: " + ex.Message });
                    return ProjectJson.Serialize(new { planned = false, id, diagnostics = ex.Message });
                }
            }
        }
        return ProjectJson.Serialize(store.Inspect(id));
    }
}