Feat v0.9.6 deprecate stage_type, collapse stage_mode

stage_type no longer validated — starlark_hook presence determines
automation. stage_mode collapsed from 4→3 values (form/delegated/
automated); "review" mapped to "form" on input for backward compat.
Migration 018 converts existing rows. Review surface removed (~110
lines). 4 package manifests updated.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-03 17:41:55 +00:00
parent 75d7abc089
commit 446edb8333
25 changed files with 121 additions and 224 deletions

View File

@@ -202,11 +202,12 @@ func (h *WorkflowHandler) CreateStage(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "name is required"})
return
}
st.StageMode = models.NormalizeStageModeInput(st.StageMode)
if st.StageMode == "" {
st.StageMode = models.StageModeDelegated
}
if !models.ValidStageModes[st.StageMode] {
c.JSON(http.StatusBadRequest, gin.H{"error": "stage_mode must be form, review, delegated, or automated"})
c.JSON(http.StatusBadRequest, gin.H{"error": "stage_mode must be form, delegated, or automated"})
return
}
if st.Audience == "" {
@@ -219,18 +220,10 @@ func (h *WorkflowHandler) CreateStage(c *gin.Context) {
if st.StageType == "" {
st.StageType = models.StageTypeSimple
}
if !models.ValidStageTypes[st.StageType] {
c.JSON(http.StatusBadRequest, gin.H{"error": "stage_type must be simple, dynamic, or automated"})
return
}
if st.StageMode == models.StageModeDelegated && st.SurfacePkgID == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "delegated mode requires surface_pkg_id"})
return
}
if (st.StageType == models.StageTypeDynamic || st.StageType == models.StageTypeAutomated) && st.StarlarkHook == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "dynamic/automated stage_type requires starlark_hook"})
return
}
if st.Ordinal == 0 {
existing, _ := h.stores.Workflows.ListStages(c.Request.Context(), st.WorkflowID)
st.Ordinal = len(existing)
@@ -252,26 +245,19 @@ func (h *WorkflowHandler) UpdateStage(c *gin.Context) {
}
st.ID = c.Param("sid")
st.WorkflowID = c.Param("id")
st.StageMode = models.NormalizeStageModeInput(st.StageMode)
if st.StageMode != "" && !models.ValidStageModes[st.StageMode] {
c.JSON(http.StatusBadRequest, gin.H{"error": "stage_mode must be form, review, delegated, or automated"})
c.JSON(http.StatusBadRequest, gin.H{"error": "stage_mode must be form, delegated, or automated"})
return
}
if st.Audience != "" && !models.ValidAudiences[st.Audience] {
c.JSON(http.StatusBadRequest, gin.H{"error": "audience must be team, public, or system"})
return
}
if st.StageType != "" && !models.ValidStageTypes[st.StageType] {
c.JSON(http.StatusBadRequest, gin.H{"error": "stage_type must be simple, dynamic, or automated"})
return
}
if st.StageMode == models.StageModeDelegated && st.SurfacePkgID == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "delegated mode requires surface_pkg_id"})
return
}
if (st.StageType == models.StageTypeDynamic || st.StageType == models.StageTypeAutomated) && st.StarlarkHook == nil {
c.JSON(http.StatusBadRequest, gin.H{"error": "dynamic/automated stage_type requires starlark_hook"})
return
}
if err := h.stores.Workflows.UpdateStage(c.Request.Context(), &st); err != nil {
c.JSON(http.StatusInternalServerError, gin.H{"error": "failed to update stage"})
return