Feat v0.6.3 dead code sweep (#38)
All checks were successful
CI/CD / detect-changes (push) Successful in 4s
CI/CD / test-frontend (push) Successful in 6s
CI/CD / test-sqlite (push) Successful in 2m46s
CI/CD / test-go-pg (push) Successful in 2m47s
CI/CD / build-and-deploy (push) Successful in 26s

Co-authored-by: Jeffrey Smith <jasafpro@gmail.com>
Co-committed-by: Jeffrey Smith <jasafpro@gmail.com>
This commit was merged in pull request #38.
This commit is contained in:
2026-03-31 12:37:47 +00:00
committed by xcaliber
parent a887b4c78b
commit 3d4228f868
130 changed files with 522 additions and 1215 deletions

View File

@@ -1,6 +1,5 @@
package handlers
// v0.38.1: Extension connection handlers — admin/global scope.
// Methods on AdminHandler, mirrors admin provider config pattern.
import (

View File

@@ -345,7 +345,6 @@ func hashToken(token string) string {
// - AdminHandler.destroyVault (admin-initiated reset)
//
// Does NOT evict from UEK cache or write audit logs — callers handle that.
// v0.29.0: accepts stores instead of using database.DB directly.
func DestroyVaultDB(ctx context.Context, stores store.Stores, userID string) (deleted int64) {
if err := stores.Users.ClearVaultKeys(ctx, userID); err != nil {
log.Printf("⚠ DestroyVaultDB: failed to clear vault columns for user %s: %v", userID, err)
@@ -366,8 +365,6 @@ func DestroyVaultDB(ctx context.Context, stores store.Stores, userID string) (de
// sessions survive server restarts.
//
// Used by BootstrapAdmin and SeedUsers where the password is known at startup.
// v0.29.0: accepts stores instead of using database.DB directly.
// v0.30.2: accepts optional uekCache to pre-warm vault on restart.
func ProbeAndRepairVault(ctx context.Context, stores store.Stores, userID, password string, uekCache ...*crypto.UEKCache) {
vaultSet, encryptedUEK, salt, nonce, err := stores.Users.GetVaultKeys(ctx, userID)
if err != nil || !vaultSet {
@@ -493,7 +490,7 @@ func BootstrapAdmin(cfg *config.Config, s store.Stores, uekCache ...*crypto.UEKC
})
// If the actual password changed, the vault seal is stale. Probe it
// with the current password and destroy only if unwrap fails.
// Ensure handle exists (backfill for pre-v0.24.0 users)
// Ensure handle exists (backfill for users)
if existing.Handle == "" {
handle := auth.UniqueHandle(ctx, s.Users, models.HandleFromName(cfg.AdminUsername))
s.Users.Update(ctx, existing.ID, map[string]interface{}{"handle": handle})

View File

@@ -1,6 +1,5 @@
package handlers
// v0.38.1: ConnectionResolver bridges the sandbox connections module
// to the store + vault layers. Implements sandbox.ConnectionResolver.
import (

View File

@@ -1,6 +1,5 @@
package handlers
// v0.38.4: Connection type discovery endpoint.
// Returns merged connection types from all active packages.
// Library declarations take precedence over non-library declarations.

View File

@@ -1,6 +1,5 @@
package handlers
// v0.38.1: Extension connection handlers — personal scope + resolution.
// Pattern follows apiconfigs.go (ProviderConfigHandler).
import (
@@ -28,7 +27,6 @@ func NewConnectionHandler(s store.Stores, vault *crypto.KeyResolver) *Connection
// ListConnections returns the user's personal connections.
func (h *ConnectionHandler) ListConnections(c *gin.Context) {
userID := getUserID(c)
// v0.38.5: Show all accessible connections (personal + team + global)
// so users can see which connections are available to them.
conns, err := h.stores.Connections.ListAccessible(c.Request.Context(), userID)
if err != nil {

View File

@@ -8,7 +8,7 @@ import (
"switchboard-core/models"
)
// ── Extension Dependencies (v0.38.2) ──────────────────
// ── Extension Dependencies ──────────────────
// Admin-only read endpoints for dependency graph visibility.
// Dependencies are created/deleted implicitly by InstallPackage/DeletePackage.

View File

@@ -1,6 +1,5 @@
// Package handlers — ext_api.go
//
// v0.29.1 CS1: Extension API routes. Starlark packages serve custom
// JSON endpoints via the existing Gin router. Mounted at
// /s/:slug/api/*path with JWT auth.
//
@@ -332,7 +331,7 @@ func hasPermission(granted []string, perm string) bool {
return false
}
// ── api_schema support (v0.6.2) ─────────────────────────────────────
// ── api_schema support ─────────────────────────────────────
// APISchemaEntry represents one route's OpenAPI-level documentation
// declared via the optional manifest "api_schema" array.

View File

@@ -1,6 +1,6 @@
package handlers
// ext_db_schema.go — v0.29.2
// ext_db_schema.go
//
// DDL generation and lifecycle management for extension-owned database tables.
// Tables are namespaced as ext_{pkg_slug}_{logical_name} and tracked in the

View File

@@ -10,7 +10,6 @@ import (
)
// ExtPermHandler serves extension permission management endpoints.
// v0.29.0 CS2: Admin reviews and grants/revokes declared permissions.
type ExtPermHandler struct {
stores store.Stores
}

View File

@@ -10,7 +10,6 @@ import (
)
// ExtSecretsHandler serves extension secret management endpoints.
// v0.29.0 CS3: Admin sets key-value secrets that Starlark extensions
// can read via the secrets.get() module.
//
// Secrets are stored in GlobalConfig under key "ext_secrets:{packageID}"

View File

@@ -16,7 +16,6 @@ import (
)
// ExtensionHandler serves extension management endpoints.
// v0.28.7: Backed by PackageStore (packages table) instead of ExtensionStore.
type ExtensionHandler struct {
stores store.Stores
}
@@ -94,7 +93,6 @@ func (h *ExtensionHandler) UpdateUserExtensionSettings(c *gin.Context) {
settings := json.RawMessage("{}")
if body.Settings != nil {
// v0.2.0: enforce user_overridable — strip locked keys before saving
schema := store.ParseSettingsSchema(pkg.Manifest)
if len(schema) > 0 {
var incoming map[string]any
@@ -229,18 +227,15 @@ func (h *ExtensionHandler) AdminInstallExtension(c *gin.Context) {
return
}
// v0.29.0: Parse manifest permissions and declare them.
// If permissions are declared, package moves to pending_review.
SyncManifestPermissions(c, h.stores, pkg.ID, manifestMap)
// v0.29.2: Create namespaced DB tables declared in the manifest.
if tables, ok := ParseDBTables(manifestMap); ok {
if err := CreateExtTables(c.Request.Context(), database.DB, database.IsPostgres(), h.stores, pkg.ID, tables); err != nil {
log.Printf("[ext-db] schema create failed for %s: %v", pkg.ID, err)
}
}
// v0.2.2: Sync triggers from manifest
SyncManifestTriggers(c.Request.Context(), h.stores, triggers.GlobalEngine(), pkg.ID, manifestMap)
c.JSON(201, gin.H{"data": pkg})
@@ -403,7 +398,6 @@ func (h *ExtensionHandler) AdminUninstallExtension(c *gin.Context) {
return
}
// v0.29.2: Drop namespaced DB tables before removing the package record.
if err := DropExtTables(c.Request.Context(), database.DB, h.stores, id); err != nil {
log.Printf("[ext-db] schema drop failed for %s: %v", id, err)
}

View File

@@ -243,7 +243,7 @@ func (h *GroupHandler) AddMember(c *gin.Context) {
return
}
// Notify the added user (v0.20.0)
// Notify the added user
if svc := notifications.Default(); svc != nil {
notifications.NotifyGroupMemberAdded(svc, req.UserID, groupID, group.Name)
}
@@ -273,7 +273,7 @@ func (h *GroupHandler) RemoveMember(c *gin.Context) {
return
}
// Notify the removed user (v0.20.0)
// Notify the removed user
if svc := notifications.Default(); svc != nil && group != nil {
notifications.NotifyGroupMemberRemoved(svc, userID, groupID, group.Name)
}

View File

@@ -62,7 +62,7 @@ func setupNotifHarness(t *testing.T) *notifHarness {
protected.PUT("/notifications/preferences/:type", notifH.SetPreference)
protected.DELETE("/notifications/preferences/:type", notifH.DeletePreference)
// Admin broadcast route (v0.28.6)
// Admin broadcast route
admin := api.Group("/admin")
admin.Use(middleware.Auth(cfg, stores.Users, userCache))
admin.Use(middleware.RequireAdmin(stores))
@@ -560,7 +560,7 @@ func TestNotifications_Unauthenticated(t *testing.T) {
}
}
// ── Admin Broadcast (v0.28.6) ────────────
// ── Admin Broadcast ────────────
func TestBroadcast_Success(t *testing.T) {
h := setupNotifHarness(t)

View File

@@ -175,7 +175,7 @@ func (h *NotificationHandler) Delete(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"ok": true})
}
// ── Notification Preferences (v0.20.0 Phase 3) ──
// ── Notification Preferences ──
// GET /api/v1/notifications/preferences
func (h *NotificationHandler) ListPreferences(c *gin.Context) {
@@ -282,7 +282,7 @@ func (h *NotificationHandler) DeletePreference(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"ok": true})
}
// ── Admin Broadcast (v0.28.6) ───────────────
// ── Admin Broadcast ───────────────
// POST /api/v1/admin/notifications/broadcast
// Admin-only: sends a system.announcement notification to all active users.

View File

@@ -1,6 +1,5 @@
// Package handlers — openapi.go
//
// v0.6.2: Dynamic OpenAPI spec generation. Merges the static kernel spec
// with extension-declared API routes. Extensions with api_schema get rich
// path items; others get auto-generated stubs.
//

View File

@@ -1,6 +1,6 @@
package handlers
// package_registry.go — v0.30.0 CS4
// package_registry.go
//
// Package registry (marketplace discovery). Admin browses available
// packages from an external JSON registry and installs them by URL.

View File

@@ -27,12 +27,12 @@ import (
var validPackageID = regexp.MustCompile(`^[a-z0-9][a-z0-9-]{0,62}[a-z0-9]$`)
// PackageHandler manages unified package lifecycle (admin-only).
// Replaces SurfaceHandler (v0.28.7).
// Replaces SurfaceHandler.
type PackageHandler struct {
stores store.Stores
packagesDir string // e.g. /data/packages — where archives are extracted
sandbox *sandbox.Sandbox // v0.30.0: for schema migration scripts
runner *sandbox.Runner // v0.38.2: for test-tool execution
sandbox *sandbox.Sandbox
runner *sandbox.Runner
}
func NewPackageHandler(s store.Stores, packagesDir ...string) *PackageHandler {
@@ -43,12 +43,12 @@ func NewPackageHandler(s store.Stores, packagesDir ...string) *PackageHandler {
return &PackageHandler{stores: s, packagesDir: dir}
}
// SetSandbox attaches a Starlark sandbox for schema migrations (v0.30.0).
// SetSandbox attaches a Starlark sandbox for schema migrations.
func (h *PackageHandler) SetSandbox(sb *sandbox.Sandbox) {
h.sandbox = sb
}
// SetRunner attaches the Starlark runner for test-tool execution (v0.38.2).
// SetRunner attaches the Starlark runner for test-tool execution.
func (h *PackageHandler) SetRunner(r *sandbox.Runner) {
h.runner = r
}
@@ -95,7 +95,6 @@ func (h *PackageHandler) GetPackage(c *gin.Context) {
func (h *PackageHandler) EnablePackage(c *gin.Context) {
id := c.Param("id")
// v0.3.7: Block enabling dormant packages (unmet requires).
pkg, err := h.stores.Packages.Get(c.Request.Context(), id)
if err != nil || pkg == nil {
c.JSON(http.StatusNotFound, gin.H{"error": "package not found"})
@@ -149,7 +148,6 @@ func (h *PackageHandler) DeletePackage(c *gin.Context) {
return
}
// v0.38.2: Libraries with active consumers cannot be uninstalled.
if pkg.Type == "library" && h.stores.Dependencies != nil {
hasConsumers, _ := h.stores.Dependencies.HasConsumers(c.Request.Context(), id)
if hasConsumers {
@@ -158,12 +156,10 @@ func (h *PackageHandler) DeletePackage(c *gin.Context) {
}
}
// v0.38.2: Clean up dependency records when uninstalling a consumer.
if h.stores.Dependencies != nil {
h.stores.Dependencies.DeleteAllForConsumer(c.Request.Context(), id)
}
// v0.29.2: Drop namespaced DB tables before removing the package record.
if err := DropExtTables(c.Request.Context(), database.DB, h.stores, id); err != nil {
log.Printf("[ext-db] schema drop failed for %s: %v", id, err)
}
@@ -195,7 +191,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
var tmpPath string
var cleanupTmp bool
// v0.30.0: Check for pre-downloaded file from registry install
if regFile, ok := c.Get("_registry_file"); ok {
tmpPath = regFile.(string)
// Don't remove — caller manages lifecycle
@@ -278,7 +273,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
return
}
// v0.38.0: Entry point validation for starlark-tier packages.
// Scripts are loaded from disk at runtime — no _starlark_script injection.
if manifestTier, _ := manifest["tier"].(string); manifestTier == "starlark" {
entryPoint := "script.star"
@@ -380,7 +374,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
c.JSON(http.StatusBadRequest, gin.H{"error": "full packages require a route"})
return
}
// v0.31.0: full packages need either server-side behavior (tools/pipes/hooks)
// or a settings schema. A surface-with-settings is a valid "full" package
// (e.g. editor: browser-tier surface + admin-configurable settings).
if !hasExtBehavior && !hasSettings {
@@ -393,7 +386,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
return
}
case "library":
// v0.38.2: Libraries must declare exports, cannot have tools/pipes/route.
exports, _ := manifest["exports"].([]any)
if len(exports) == 0 {
c.JSON(http.StatusBadRequest, gin.H{"error": "library packages require an 'exports' array"})
@@ -471,7 +463,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
userID := c.GetString("user_id")
// v0.30.0: use registry source if set by registry install handler
pkgSource := "extension"
if src, ok := c.Get("_registry_source"); ok {
pkgSource = src.(string)
@@ -509,22 +500,18 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
log.Printf("[packages] Failed to update package metadata for %s: %v", pkgID, err)
}
// v0.29.2: Create namespaced DB tables declared in the manifest.
if tables, ok := ParseDBTables(manifest); ok {
if err := CreateExtTables(c.Request.Context(), database.DB, database.IsPostgres(), h.stores, pkgID, tables); err != nil {
log.Printf("[ext-db] schema create failed for %s: %v", pkgID, err)
}
}
// v0.38.5: Declare manifest permissions (same as AdminInstallExtension).
// This creates the permission rows and sets status to pending_review
// if the package declares permissions.
SyncManifestPermissions(c, h.stores, pkgID, manifest)
// v0.2.2: Sync triggers from manifest
SyncManifestTriggers(c.Request.Context(), h.stores, triggers.GlobalEngine(), pkgID, manifest)
// v0.30.0: Run schema migrations if declared.
newSchemaVersion := ParseSchemaVersion(manifest)
if newSchemaVersion > 0 && h.sandbox != nil {
oldSchemaVersion := 0
@@ -553,7 +540,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
}
}
// v0.30.2: Install workflow definition from package manifest.
if pkgType == "workflow" {
if err := InstallWorkflowFromManifest(c, h.stores, pkgID, manifest); err != nil {
log.Printf("[packages] workflow install failed for %s: %v", pkgID, err)
@@ -562,7 +548,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
}
}
// v0.38.2: Create dependency records from manifest "dependencies" map.
// Libraries must be installed first; consumers declare them.
if deps, ok := manifest["dependencies"].(map[string]any); ok && len(deps) > 0 {
// Clear stale dependency records from a previous install of the same consumer.
@@ -603,7 +588,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
log.Printf("[packages] %s: %d dependencies recorded", pkgID, len(deps))
}
// v0.2.1: Auto-set default_surface when the first extension surface is installed.
if (pkgType == "surface" || pkgType == "full") && pkgSource != "core" {
if raw, err := h.stores.GlobalConfig.Get(c.Request.Context(), "default_surface"); err != nil || raw == nil {
dflt := models.JSONMap{"id": pkgID}
@@ -615,7 +599,6 @@ func (h *PackageHandler) InstallPackage(c *gin.Context) {
}
}
// v0.3.7: Auto-set dormant for packages with unmet requires.
// Known capabilities: (none yet — chat and legacy-sdk are future/removed).
knownCaps := map[string]bool{}
var unmetReqs []string
@@ -689,7 +672,7 @@ func extractableRelPath(name string) string {
return ""
}
// ── Package settings (v0.30.0 CS2) ──────────────
// ── Package settings ──────────────
// GetPackageSettings returns the settings schema from the manifest merged
// with current admin-configured values.
@@ -823,7 +806,7 @@ func (h *PackageHandler) ListEnabledSurfaces(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"data": enabled})
}
// ── Test Tool (v0.38.2) ─────────────────────────
// ── Test Tool ─────────────────────────
// POST /admin/packages/:id/test-tool
// Invokes a starlark extension's on_tool_call entry point directly,
// without going through the AI chat loop. Admin-only test harness.
@@ -898,7 +881,7 @@ func (h *PackageHandler) TestTool(c *gin.Context) {
})
}
// ── Package update (v0.5.4) ──────────────────────
// ── Package update ──────────────────────
// UpdatePackage applies an in-place update to an existing package.
// POST /api/v1/admin/packages/:id/update
@@ -1175,7 +1158,7 @@ func mergePackageSettings(existing *store.PackageRegistration, manifest map[stri
return data
}
// ── Package export (v0.5.4) ──────────────────────
// ── Package export ──────────────────────
// ExportPackage exports an installed package as a downloadable .pkg archive.
// GET /api/v1/admin/packages/:id/export

View File

@@ -23,23 +23,16 @@ import (
)
// defaultBundledPackages is the curated set of packages installed by default.
// This is the dev/test bundle — includes demos and workflow examples.
// Other packages still ship in the Docker image but require BUNDLED_PACKAGES
// to be set explicitly (or "*" for all).
//
// Recommended production override (lean):
// BUNDLED_PACKAGES=notes,chat,chat-core,cluster-dashboard
// Recommended production override: BUNDLED_PACKAGES=notes,chat,chat-core,mermaid-renderer,schedules
var defaultBundledPackages = map[string]bool{
"notes": true,
"chat": true,
"chat-core": true,
"workflow-chat": true,
"dashboard": true,
"cluster-dashboard": true,
"workflow-demo": true,
"bug-report-triage": true,
"content-approval": true,
"employee-onboarding": true,
"notes": true,
"chat": true,
"chat-core": true,
"mermaid-renderer": true,
"schedules": true,
}
// InstallBundledPackages scans bundledDir for .pkg archives and installs

View File

@@ -1,12 +1,11 @@
package handlers
// presence.go — Heartbeat upsert and status query (v0.23.1)
// presence.go — Heartbeat upsert and status query
//
// Clients POST /api/v1/presence/heartbeat every 30s while active.
// GET /api/v1/presence?users=id1,id2 returns current status.
// Online = last_seen within 90s.
//
// v0.29.0: Raw SQL replaced with PresenceStore + UserStore methods.
import (
"net/http"

View File

@@ -8,7 +8,6 @@ package handlers
// (profile, permissions, teams/mine, settings) into one call.
// The SDK calls this at startup and on token refresh.
//
// v0.37.15
import (
"net/http"

View File

@@ -51,7 +51,7 @@ func (s *jsonScanner) Scan(src interface{}) error {
// memory on the next rows.Scan() call, corrupting our data after the
// fact. This caused intermittent SafeJSON 500s on paginated list
// endpoints where row N's Settings pointed to row N+1's overwritten
// buffer. Fixed in v0.21.7.
// buffer. Fixed.
cp := make([]byte, len(v))
copy(cp, v)
*s.dest = json.RawMessage(cp)

View File

@@ -185,7 +185,7 @@ func TestSafeJSON_PaginatedWithCorrupt(t *testing.T) {
}
// ── Driver buffer aliasing tests ────────────
// These verify the fix for the v0.21.7 corruption bug: scanJSON must
// These verify the fix for the buffer-aliasing corruption bug: scanJSON must
// copy []byte from the database driver, not alias it. Without the copy,
// rows.Next() can overwrite the buffer and corrupt previously-scanned
// json.RawMessage values in a paginated list.

View File

@@ -2,7 +2,6 @@ package handlers
// settings.go — User profile, preferences, and password management.
//
// v0.29.0: Raw SQL replaced with UserStore methods.
import (
"context"

View File

@@ -1,6 +1,6 @@
package handlers
// team_package_settings.go — v0.2.0
// team_package_settings.go
//
// Team-admin endpoints for managing team-scoped package settings.
// These sit in the settings cascade between global (admin) and user.

View File

@@ -419,7 +419,7 @@ func (h *TeamHandler) ListTeamAuditActions(c *gin.Context) {
c.JSON(http.StatusOK, gin.H{"actions": actions})
}
// ── Team Roles API (v0.3.4) ─────────────────
// ── Team Roles API ─────────────────
// builtinRoles are always present in every team's role list.
var builtinRoles = []string{"admin", "member"}

View File

@@ -14,7 +14,7 @@ import (
)
// ═══════════════════════════════════════════════
// Upgrade Tests — v0.5.5
// Upgrade Tests
//
// Schema edge cases, settings migration, and
// package compatibility across kernel upgrades.

View File

@@ -1,6 +1,6 @@
package handlers
// user_packages.go — v0.30.0 CS5
// user_packages.go
//
// Non-admin package management. Users install personal-scoped packages;
// team admins install team-scoped packages.

View File

@@ -1,6 +1,6 @@
package handlers
// workflow_packages.go — v0.30.2 CS1
// workflow_packages.go
//
// Handles workflow-specific package operations:
// - ExportWorkflowPackage: bundles a workflow definition + stages into a .pkg

View File

@@ -11,7 +11,7 @@ import (
"switchboard-core/workflow"
)
// ── Public Workflow Handlers (v0.3.3) ───────
// ── Public Workflow Handlers ───────
// WorkflowPublicHandler manages unauthenticated workflow entry endpoints.
type WorkflowPublicHandler struct {

View File

@@ -10,7 +10,7 @@ import (
"switchboard-core/workflow"
)
// ── Signoff Handlers (v0.3.4) ─────────────────
// ── Signoff Handlers ─────────────────
// WorkflowSignoffHandler manages signoff HTTP endpoints.
type WorkflowSignoffHandler struct {

View File

@@ -603,7 +603,7 @@ func TestWorkflowAssignment_Cancel(t *testing.T) {
}
}
// ── v0.3.3 Store Tests ──────────────────────
// ── Instance Lifecycle Store Tests ──────────────────────
func TestWorkflowInstance_MarkStale(t *testing.T) {
database.RequireTestDB(t)
@@ -779,7 +779,7 @@ func TestWorkflowAssignment_ListByTeam(t *testing.T) {
}
}
// ── Signoff store tests (v0.3.4) ──────────────
// ── Signoff store tests ──────────────
func TestWorkflowSignoff_CreateAndList(t *testing.T) {
database.RequireTestDB(t)
@@ -862,7 +862,7 @@ func TestWorkflowSignoff_ListEmpty(t *testing.T) {
}
}
// ── Clone tests (v0.3.5) ──────────────────
// ── Clone tests ──────────────────
func TestWorkflow_Clone(t *testing.T) {
database.RequireTestDB(t)

View File

@@ -9,7 +9,7 @@ import (
"switchboard-core/models"
)
// ── Team-Scoped Workflow Wrappers (v0.31.2) ────────────────
// ── Team-Scoped Workflow Wrappers ────────────────
//
// Thin wrappers that enforce team ownership before delegating
// to the existing WorkflowHandler methods. Follows the same

View File

@@ -106,7 +106,6 @@ func (h *WorkflowHandler) Create(c *gin.Context) {
w.CreatedBy = c.GetString("user_id")
// v0.31.2: team-scoped route injects force_team_id via CreateTeamWorkflow
if ftid, ok := c.Get("force_team_id"); ok {
tid := ftid.(string)
w.TeamID = &tid
@@ -417,7 +416,6 @@ func (h *WorkflowHandler) Clone(c *gin.Context) {
CreatedBy: c.GetString("user_id"),
}
// v0.31.2: team-scoped route injects force_team_id
if ftid, ok := c.Get("force_team_id"); ok {
tid := ftid.(string)
clone.TeamID = &tid