From dadd461a716c85d058e462ac35d41174875f0619 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 05:36:06 +0000 Subject: [PATCH 1/3] Initial plan From 791f74a054fb1077ee565c654574efebd6a4b980 Mon Sep 17 00:00:00 2001 From: "copilot-swe-agent[bot]" <198982749+Copilot@users.noreply.github.com> Date: Mon, 23 Feb 2026 05:43:04 +0000 Subject: [PATCH 2/3] fix(server): address multi-workflow mode review feedback Co-authored-by: intel352 <77607+intel352@users.noreply.github.com> --- api/router.go | 6 +++ api/workflow_handler.go | 64 ++++++++++++++++++++++------ cmd/server/data/featureflags.db-shm | Bin 0 -> 32768 bytes cmd/server/main.go | 23 +++++++--- 4 files changed, 74 insertions(+), 19 deletions(-) create mode 100644 cmd/server/data/featureflags.db-shm diff --git a/api/router.go b/api/router.go index 234817f4..b990c1fb 100644 --- a/api/router.go +++ b/api/router.go @@ -22,6 +22,11 @@ type Config struct { // OAuth providers keyed by provider name (e.g. "google", "okta"). OAuthProviders map[string]*OAuthProviderConfig + + // Orchestrator is an optional engine manager that is called when workflows + // are deployed or stopped via the API. When nil, only the database status + // is updated (no live engine is started or stopped). + Orchestrator WorkflowOrchestrator } // Stores groups all store interfaces needed by the API. @@ -109,6 +114,7 @@ func NewRouterWithIAM(stores Stores, cfg Config, iamResolver *iam.IAMResolver) h // --- Workflows --- wfH := NewWorkflowHandler(stores.Workflows, stores.Projects, stores.Memberships, permissions) + wfH.orchestrator = cfg.Orchestrator mux.Handle("POST /api/v1/projects/{pid}/workflows", mw.RequireAuth(http.HandlerFunc(wfH.Create))) mux.Handle("GET /api/v1/workflows", mw.RequireAuth(http.HandlerFunc(wfH.ListAll))) mux.Handle("GET /api/v1/projects/{pid}/workflows", mw.RequireAuth(http.HandlerFunc(wfH.ListInProject))) diff --git a/api/workflow_handler.go b/api/workflow_handler.go index 3a2aeb32..497ba319 100644 --- a/api/workflow_handler.go +++ b/api/workflow_handler.go @@ -1,6 +1,7 @@ package api import ( + "context" "encoding/json" "errors" "net/http" @@ -11,12 +12,21 @@ import ( "github.com/google/uuid" ) +// WorkflowOrchestrator is implemented by the multi-workflow engine manager to +// allow the API layer to start and stop live workflow engines. It is optional; +// when nil the Deploy and Stop handlers only update the database status. +type WorkflowOrchestrator interface { + DeployWorkflow(ctx context.Context, workflowID uuid.UUID) error + StopWorkflow(ctx context.Context, workflowID uuid.UUID) error +} + // WorkflowHandler handles workflow CRUD and lifecycle endpoints. type WorkflowHandler struct { - workflows store.WorkflowStore - projects store.ProjectStore - memberships store.MembershipStore - permissions *PermissionService + workflows store.WorkflowStore + projects store.ProjectStore + memberships store.MembershipStore + permissions *PermissionService + orchestrator WorkflowOrchestrator // optional; nil when no engine manager is wired } // NewWorkflowHandler creates a new WorkflowHandler. @@ -259,11 +269,24 @@ func (h *WorkflowHandler) Deploy(w http.ResponseWriter, r *http.Request) { WriteError(w, http.StatusInternalServerError, "internal error") return } - wf.Status = store.WorkflowStatusActive - wf.UpdatedAt = time.Now() - if err := h.workflows.Update(r.Context(), wf); err != nil { - WriteError(w, http.StatusInternalServerError, "internal error") - return + if h.orchestrator != nil { + if oErr := h.orchestrator.DeployWorkflow(r.Context(), id); oErr != nil { + WriteError(w, http.StatusInternalServerError, oErr.Error()) + return + } + // Re-fetch to get updated status written by the orchestrator. + wf, err = h.workflows.Get(r.Context(), id) + if err != nil { + WriteError(w, http.StatusInternalServerError, "internal error") + return + } + } else { + wf.Status = store.WorkflowStatusActive + wf.UpdatedAt = time.Now() + if err := h.workflows.Update(r.Context(), wf); err != nil { + WriteError(w, http.StatusInternalServerError, "internal error") + return + } } WriteJSON(w, http.StatusOK, wf) } @@ -284,11 +307,24 @@ func (h *WorkflowHandler) Stop(w http.ResponseWriter, r *http.Request) { WriteError(w, http.StatusInternalServerError, "internal error") return } - wf.Status = store.WorkflowStatusStopped - wf.UpdatedAt = time.Now() - if err := h.workflows.Update(r.Context(), wf); err != nil { - WriteError(w, http.StatusInternalServerError, "internal error") - return + if h.orchestrator != nil { + if oErr := h.orchestrator.StopWorkflow(r.Context(), id); oErr != nil { + WriteError(w, http.StatusInternalServerError, oErr.Error()) + return + } + // Re-fetch to get updated status written by the orchestrator. + wf, err = h.workflows.Get(r.Context(), id) + if err != nil { + WriteError(w, http.StatusInternalServerError, "internal error") + return + } + } else { + wf.Status = store.WorkflowStatusStopped + wf.UpdatedAt = time.Now() + if err := h.workflows.Update(r.Context(), wf); err != nil { + WriteError(w, http.StatusInternalServerError, "internal error") + return + } } WriteJSON(w, http.StatusOK, wf) } diff --git a/cmd/server/data/featureflags.db-shm b/cmd/server/data/featureflags.db-shm new file mode 100644 index 0000000000000000000000000000000000000000..a71f371017e8e8b9dd20ec4415823ad748b7be2b GIT binary patch literal 32768 zcmeI)Ee^s!5C+gKls^TE)S(e5?!Xn$Xik8{A~*w%8*mT|4uQvFVK*sBB}mx!C7Yc# z&9?LEr@GC;J4FbiLA>b2wV2;0`^R*5*_@V#?W$N`&&SSqc&xsB{(W{j_t{AEeyZcU zLo2R!U6*n1)O9!R>6?TA0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAVA>X1d33aEb(tYRX-mJq Date: Mon, 23 Feb 2026 05:43:47 +0000 Subject: [PATCH 3/3] fix(gitignore): fix malformed db-shm pattern; untrack sqlite wal file Co-authored-by: intel352 <77607+intel352@users.noreply.github.com> --- .gitignore | 3 ++- cmd/server/data/featureflags.db-shm | Bin 32768 -> 0 bytes 2 files changed, 2 insertions(+), 1 deletion(-) delete mode 100644 cmd/server/data/featureflags.db-shm diff --git a/.gitignore b/.gitignore index 19bc47e8..36bd0a4a 100644 --- a/.gitignore +++ b/.gitignore @@ -46,5 +46,6 @@ node_modules/ # Built admin UI assets (generated by `make build-ui`, embedded via go:embed) module/ui_dist/* -!module/ui_dist/.gitkeep*.db-shm +!module/ui_dist/.gitkeep +*.db-shm *.db-wal diff --git a/cmd/server/data/featureflags.db-shm b/cmd/server/data/featureflags.db-shm deleted file mode 100644 index a71f371017e8e8b9dd20ec4415823ad748b7be2b..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 32768 zcmeI)Ee^s!5C+gKls^TE)S(e5?!Xn$Xik8{A~*w%8*mT|4uQvFVK*sBB}mx!C7Yc# z&9?LEr@GC;J4FbiLA>b2wV2;0`^R*5*_@V#?W$N`&&SSqc&xsB{(W{j_t{AEeyZcU zLo2R!U6*n1)O9!R>6?TA0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAV7cs z0RjXF5FkK+009C72oNAZfB*pk1PBlyK!5-N0t5&UAVA>X1d33aEb(tYRX-mJq