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 } }