Files
Agent-System/internal/app/intentflow/flags.go
weichenandCursor 9364da4974 feat(intentflow): freeze plan lifecycle contracts and batch policy
Add the app/intentflow skeleton so HITL state, error codes, REST shapes, and BatchActionPolicy live outside intent and default to the legacy tier.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-15 16:22:52 +00:00

90 lines
1.6 KiB
Go

package intentflow
import (
"os"
"strings"
"sync"
)
// PlanTier is the intentflow rollout switch. It must not be stored on
// intent.ActionSpec or intent.FeatureFlags.
type PlanTier string
const (
TierLegacy PlanTier = "legacy"
TierShadow PlanTier = "shadow"
TierAutoRead PlanTier = "auto_read"
TierConfirm PlanTier = "confirm"
TierFull PlanTier = "full"
)
type FeatureFlags struct {
PlanTier PlanTier
}
var (
flagsMu sync.RWMutex
activeFlags = defaultFeatureFlags()
)
func defaultFeatureFlags() FeatureFlags {
return FeatureFlags{PlanTier: parsePlanTier(os.Getenv("MULTI_INTENT_PLAN_TIER"))}
}
func parsePlanTier(raw string) PlanTier {
switch strings.ToLower(strings.TrimSpace(raw)) {
case "", "legacy":
return TierLegacy
case "shadow":
return TierShadow
case "auto_read", "autoread":
return TierAutoRead
case "confirm":
return TierConfirm
case "full":
return TierFull
default:
return TierLegacy
}
}
func CurrentFeatureFlags() FeatureFlags {
flagsMu.RLock()
defer flagsMu.RUnlock()
return activeFlags
}
func SetFeatureFlagsForTest(f FeatureFlags) {
flagsMu.Lock()
activeFlags = f
flagsMu.Unlock()
}
func ResetFeatureFlagsForTest() {
flagsMu.Lock()
activeFlags = defaultFeatureFlags()
flagsMu.Unlock()
}
// Allows reports whether the current (or supplied) tier may activate a policy row.
func (t PlanTier) Allows(min PlanTier) bool {
return tierRank(t) >= tierRank(min)
}
func tierRank(t PlanTier) int {
switch t {
case TierLegacy:
return 0
case TierShadow:
return 1
case TierAutoRead:
return 2
case TierConfirm:
return 3
case TierFull:
return 4
default:
return 0
}
}