package transport import ( "encoding/json" "fmt" "os" ) // Kind identifies which messaging fabric a bot runs on. type Kind string const ( // KindMatrix is the proven Matrix (mautrix) path — the default, so master // stays on the battle-tested transport. KindMatrix Kind = "matrix" // KindUnibus routes the bot over the unibus message bus. KindUnibus Kind = "unibus" ) // Select chooses a bot's transport. unibus is used only when the global feature // flag is enabled AND the bot has opted in; otherwise Matrix. This is the // branch-by-abstraction toggle: with the flag on, bots migrate to unibus one at // a time by opting in, while every other bot keeps speaking Matrix unchanged. func Select(flagEnabled, botOptIn bool) Kind { if flagEnabled && botOptIn { return KindUnibus } return KindMatrix } // flagsFile mirrors dev/feature_flags.json (see .claude rule feature_flags.md). type flagsFile struct { Flags map[string]struct { Enabled bool `json:"enabled"` Issue string `json:"issue"` Description string `json:"description"` } `json:"flags"` } // FlagEnabled reports whether the named feature flag is enabled in the given // dev/feature_flags.json file. A missing file or missing flag reads as false // (fail-safe: default to the Matrix path), not an error — only malformed JSON // surfaces an error. func FlagEnabled(path, name string) (bool, error) { data, err := os.ReadFile(path) if err != nil { if os.IsNotExist(err) { return false, nil } return false, fmt.Errorf("transport: read flags %q: %w", path, err) } var f flagsFile if err := json.Unmarshal(data, &f); err != nil { return false, fmt.Errorf("transport: parse flags %q: %w", path, err) } return f.Flags[name].Enabled, nil }