Repository navigation
feat(reborn): add telegram v2 default-off config guard - #3356
nickpismenkov merged 6 commits into
Conversation
Split from PR #3316. Adds the default-off REBORN_TELEGRAM_V2_ENABLED marker, config plumbing, and fail-closed v1/v2 Telegram exclusivity validator without registering production v2 routes. Co-authored-by: Nikolay Pismenkov <nickpismenkov@gmail.com>
There was a problem hiding this comment.
Code Review
This pull request introduces a feature flag, REBORN_TELEGRAM_V2_ENABLED, to manage the transition between legacy Telegram v1 and the new v2 ProductAdapter, including a validation utility to ensure mutual exclusivity. Feedback suggests integrating this validation directly into the configuration resolution process and refactoring the validator to accept a configuration reference, which would encapsulate the activation logic and reduce the risk of logic drift.
| } | ||
| ids | ||
| }, | ||
| reborn_telegram_v2_enabled: parse_bool_env("REBORN_TELEGRAM_V2_ENABLED", false)?, |
There was a problem hiding this comment.
Consider calling validate_telegram_v1_v2_exclusivity directly within ChannelsConfig::resolve. Enforcing configuration invariants during resolution ensures that an invalid configuration state is caught as early as possible (e.g., during Config::from_env or Config::from_db). When implementing this, ensure you use a specific error variant like MalformedConfig for these configuration errors to follow the repository's preflight check standards.
References
- When adding preflight configuration checks, introduce specific error variants (e.g., MalformedConfig) for configuration errors rather than reusing existing, potentially misleading variants.
| pub fn validate_telegram_v1_v2_exclusivity( | ||
| v1_active: bool, | ||
| v2_active: bool, | ||
| ) -> Result<(), ConfigError> { |
There was a problem hiding this comment.
The logic for determining if the legacy Telegram v1 channel is active (checking both wasm_channels_enabled and configured_wasm_channels) is currently described in the docstring but left to the caller to implement. To prevent logic drift and ensure consistency across potential call sites, consider changing this function to take a reference to ChannelsConfig and encapsulating that check within the function body. This would make the validator more robust and easier to use correctly.
…Config::resolve Two related changes addressing gemini-code-assist review comments on PR #3356: 1. Wire `validate_telegram_v1_v2_exclusivity` into `ChannelsConfig::resolve`. The validator was exported but had no production caller — only its own unit tests invoked it. The intent (per CLAUDE.md and the issue body) was that startup would call it before binding the telegram webhook route. Moving the call inside `resolve` enforces the invariant eagerly during `Config::from_env` / `Config::from_db` rather than relying on a yet-to-be-written startup gate, and gives every consumer of `ChannelsConfig` the guarantee for free. 2. Change the validator signature from `(v1_active: bool, v2_active: bool)` to `(channels: &ChannelsConfig)`. The "v1 active" rule (wasm_channels enabled AND telegram listed in `configured_wasm_channels`) was documented in the docstring and left to the caller — a classic logic-drift seam. Encapsulating both axes inside the validator collapses them to one source of truth. Updates the 4 existing unit tests to construct minimal `ChannelsConfig` fixtures, adds 3 new cases covering the previously-uncovered partial-v1 states (telegram listed but wasm disabled; wasm enabled but telegram not listed) plus a `resolve`-end-to-end regression test that drives the full env→ConfigError path. Rewrites `tests/telegram_v2_default_off_integration.rs` against the new `&ChannelsConfig` shape. `ConfigError::InvalidValue { key, message }` is the right variant here and is preserved unchanged — there is no `MalformedConfig` variant in this codebase, despite the bot's suggestion. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Followup to cfd4a3b — two `validate_telegram_v1_v2_exclusivity` calls chained with `.expect`/`.expect_err` exceeded rustfmt's line width. Wrap the call and the .expect onto separate lines to satisfy `cargo fmt --all -- --check`. No behavior change. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
henrypark133
left a comment
There was a problem hiding this comment.
Finding: the Telegram v1/v2 exclusivity guard misses persisted and hot-activated legacy Telegram channels.
validate_telegram_v1_v2_exclusivity only treats v1 as active when wasm_channels_enabled is true and configured_wasm_channels contains "telegram". But configured_wasm_channels is documented as the setup-wizard startup fallback, separate from runtime activated_channels. Startup loads persisted active WASM channel names from the extension manager, passes them into setup_wasm_channels, and auto-activates persisted WASM channels independently of configured_wasm_channels.
That leaves an existing installation with legacy Telegram active via persisted activated_channels able to start with REBORN_TELEGRAM_V2_ENABLED=true and bypass the fail-closed guard, despite both paths being active for the same webhook installation. Please move/extend the guard to the runtime activation path as well, or feed persisted active channel state into the startup validation, and add coverage for the persisted-active Telegram case.
There was a problem hiding this comment.
Pull request overview
Adds default-off configuration plumbing for the Reborn Telegram v2 ProductAdapter and enforces a fail-closed startup invariant that prevents v1 (legacy WASM channel) and v2 from being enabled for the same Telegram installation at the same time.
Changes:
- Introduce
REBORN_TELEGRAM_V2_ENABLED(defaultfalse) and plumb it throughChannelsConfig::reborn_telegram_v2_enabled. - Add
validate_telegram_v1_v2_exclusivity()and invoke it fromChannelsConfig::resolve()to enforce v1/v2 mutual exclusivity at config resolution time. - Add integration + unit tests to pin the default-off behavior and exclusivity matrix; update test helpers to include the new config field.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/telegram_v2_default_off_integration.rs | Adds caller-level integration coverage for the v1/v2 exclusivity validator and default-off contract. |
| src/tunnel/mod.rs | Updates channel-config test helpers to include the new reborn_telegram_v2_enabled field. |
| src/config/mod.rs | Re-exports the validator and ensures Config::for_testing() defaults v2 to disabled. |
| src/config/channels.rs | Adds reborn_telegram_v2_enabled, parses REBORN_TELEGRAM_V2_ENABLED, and enforces v1/v2 exclusivity during resolve; adds unit tests. |
| Cargo.toml | Extends workspace exclude list (now includes a v2 Telegram path). |
| .env.example | Documents the new default-off env flag and the fail-closed startup behavior. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let err = validate_telegram_v1_v2_exclusivity(&channels_cfg(true, true, true)) | ||
| .expect_err("simultaneous v1+v2 must reject"); | ||
| let rendered = err.to_string(); | ||
| assert!(rendered.contains("REBORN_TELEGRAM_V2_ENABLED")); | ||
| assert!(rendered.contains("3285")); | ||
| } |
| fn resolve_rejects_v1_and_v2_telegram_together() { | ||
| let _guard = lock_env(); | ||
| // SAFETY: under ENV_MUTEX | ||
| unsafe { std::env::set_var("REBORN_TELEGRAM_V2_ENABLED", "true") }; | ||
| let mut settings = Settings::default(); | ||
| settings.channels.wasm_channels_enabled = true; | ||
| settings.channels.wasm_channels = vec!["telegram".to_string()]; | ||
| let err = ChannelsConfig::resolve(&settings, "owner").expect_err("must reject"); | ||
| assert!( | ||
| matches!(err, ConfigError::InvalidValue { ref key, .. } if key == "REBORN_TELEGRAM_V2_ENABLED"), | ||
| "expected REBORN_TELEGRAM_V2_ENABLED InvalidValue, got: {err:?}" | ||
| ); | ||
| // SAFETY: under ENV_MUTEX | ||
| unsafe { std::env::remove_var("REBORN_TELEGRAM_V2_ENABLED") }; | ||
| } |
| "channels-src/telegram", | ||
| "channels-src/slack", | ||
| "channels-src/whatsapp", | ||
| "channels-src-v2/telegram", |
Henry's review on PR #3356 flagged a startup-bypass: the exclusivity guard ran only at `ChannelsConfig::resolve` time and only consulted the env-var view of v1 (`configured_wasm_channels`). But persisted `activated_channels` rows can carry `telegram` independently of `WASM_CHANNELS`, and `setup_wasm_channels` auto-loads persisted-active channels at startup. So a deployment with: - `REBORN_TELEGRAM_V2_ENABLED=true` - `WASM_CHANNELS` env var that does NOT list `telegram` - persisted `activated_channels` row that DOES list `telegram` …would pass the env-tier guard, then have both v1 (via persisted auto-activate) and v2 (via env flag) stand up for the same Telegram webhook installation. Fix: - `validate_telegram_v1_v2_exclusivity` gains an `Option<&HashSet<String>>` argument carrying the persisted-active WASM channel set. v1 is now considered active when `wasm_channels_enabled && (telegram listed in env OR telegram in persisted-active)`. The Option lets `ChannelsConfig::resolve` keep calling it (env-only) without inventing fake state. - `src/main.rs` re-runs the validator with `Some(&startup_active_wasm_channels)` right after the set is computed and before `setup_wasm_channels` fires. Fail-closed: a hit returns the same `ConfigError::InvalidValue` via `?` and aborts startup. - Three new unit tests in `config::channels::telegram_v2_tests` and three new caller-tier tests in `tests/telegram_v2_default_off_integration.rs` cover the new axis: persisted telegram + v2 blocks; persisted non-telegram + v2 allows; persisted telegram with `wasm_channels_enabled=false` allows (setup doesn't run, the set is dormant). Other reviewer findings addressed: - Copilot #1 (`tests/.../integration.rs:61`): replaced the brittle `Display`-substring assertion with a structured `match err { ConfigError::InvalidValue { key, .. } => ... }`. Wording changes to the message no longer break the test. - Copilot #2 (`channels.rs:582`): the unsafe `set_var` / `remove_var` pair in `resolve_rejects_v1_and_v2_telegram_together` clobbered any pre-existing developer-shell value. Replaced with a local `ScopedEnv` RAII guard that saves the previous value on `set` and restores it on drop (still gated by `lock_env()` for cross-test serialization).
|
Pushed @henrypark133 — persisted-active v1 gap (your finding): You're right — the env-only guard let an installation with persisted Fix:
@copilot — inline findings:
@gemini-code-assist: both findings — (1) fold the validator call into 10 unit tests + 10 integration tests pass; |
Henry's review on PR #3356 flagged a startup-bypass: the exclusivity guard ran only at `ChannelsConfig::resolve` time and only consulted the env-var view of v1 (`configured_wasm_channels`). But persisted `activated_channels` rows can carry `telegram` independently of `WASM_CHANNELS`, and `setup_wasm_channels` auto-loads persisted-active channels at startup. So a deployment with: - `REBORN_TELEGRAM_V2_ENABLED=true` - `WASM_CHANNELS` env var that does NOT list `telegram` - persisted `activated_channels` row that DOES list `telegram` …would pass the env-tier guard, then have both v1 (via persisted auto-activate) and v2 (via env flag) stand up for the same Telegram webhook installation. Fix: - `validate_telegram_v1_v2_exclusivity` gains an `Option<&HashSet<String>>` argument carrying the persisted-active WASM channel set. v1 is now considered active when `wasm_channels_enabled && (telegram listed in env OR telegram in persisted-active)`. The Option lets `ChannelsConfig::resolve` keep calling it (env-only) without inventing fake state. - `src/main.rs` re-runs the validator with `settings_persistence_available.then_some(&persisted_active_wasm_channels)` right before `setup_wasm_channels` fires. Fail-closed: a hit returns the same `ConfigError::InvalidValue` via `?` and aborts startup. - Three new unit tests in `config::channels::telegram_v2_tests` and three new caller-tier tests in `tests/telegram_v2_default_off_integration.rs` cover the new axis: persisted telegram + v2 blocks; persisted non-telegram + v2 allows; persisted telegram with `wasm_channels_enabled=false` allows (setup doesn't run, the set is dormant). Other reviewer findings addressed: - Copilot #1 (`tests/.../integration.rs:61`): replaced the brittle `Display`-substring assertion with a structured `match err { ConfigError::InvalidValue { key, .. } => ... }`. Wording changes to the message no longer break the test. - Copilot #2 (`channels.rs:582`): the unsafe `set_var` / `remove_var` pair in `resolve_rejects_v1_and_v2_telegram_together` clobbered any pre-existing developer-shell value. Replaced with a local `ScopedEnv` RAII guard that saves the previous value on `set` and restores it on drop (still gated by `lock_env()` for cross-test serialization).
284eff0 to
6f24585
Compare
Resolves merge conflicts against the PR's base branch: - `Cargo.toml`: drop dead `channels-src-v2/telegram` exclude (path does not exist on this branch); take `crates/ironclaw_silk_decoder` from the base (Copilot #3 finding). - `src/main.rs`: the base refactored where the persisted-active WASM channel set is computed (now `startup_active_wasm_channels`, resolved inside the `wasm_channels_enabled` block via `load_startup_active_channels` + `startup_active_wasm_channel_names`). Keep that resolution, then re-run the v1/v2 exclusivity validator with `Some(&startup_active_wasm_channels)` on the new variable name (still ahead of `setup_wasm_channels`, still fail-closed). No behavior change beyond reconciling the two histories.
| .configured_wasm_channels | ||
| .iter() | ||
| .any(|c| c == "telegram"); | ||
| let v1_telegram_persisted = | ||
| persisted_active_wasm_channels.is_some_and(|active| active.iter().any(|c| c == "telegram")); |
henrypark133
left a comment
There was a problem hiding this comment.
Thanks for the follow-up fixes here. I rechecked the current head and the earlier persisted-startup bypass is fixed: startup now re-runs the Telegram v1/v2 exclusivity validator with the persisted activated_channels set before setup_wasm_channels, and the new tests cover the persisted-active Telegram case.
What looks good:
ChannelsConfig::resolvenow rejects env-configured v1 Telegram plusREBORN_TELEGRAM_V2_ENABLED=true.- Startup now rejects persisted-active
telegramplus v2 before the legacy WASM channel is restored. - The tests cover the persisted-active Telegram case and avoid brittle error-string assertions.
- CI is meaningful and green, and the targeted local tests below passed.
Concerning: hot activation can still bypass the v1/v2 exclusivity guard
The exclusivity check only runs during config resolution and startup. ExtensionManager::activate_wasm_channel can still later activate the legacy telegram WASM channel and persist it without knowing whether reborn_telegram_v2_enabled is true.
That leaves a remaining sequence where the process starts cleanly with v2 enabled and no active v1 channel, then a user activates the legacy telegram extension through /api/extensions/telegram/activate or the equivalent extension activation path. At that point both paths can be enabled for the same Telegram installation despite the startup guard.
Please thread the v2 flag or a small activation policy into the extension/channel activation path and reject activation of the legacy telegram WASM channel when v2 is enabled. Add caller-level coverage for the activation path, not just the validator helper.
Checks I ran:
git diff --check origin/review-base-pr3356...HEADcargo test --test telegram_v2_default_off_integration -- --nocapturecargo test -p ironclaw config::channels::telegram_v2_tests --lib -- --nocapturecargo test -p ironclaw extensions::manager::tests::test_activate_wasm_channel_finds_legacy_hyphen_alias --lib -- --nocapture
Henry's CHANGES_REQUESTED review on PR #3356 flagged that the startup exclusivity guard does not protect against runtime activation: a clean v2-only start followed by a `/api/extensions/telegram/activate` (or the equivalent `ToolDispatcher::dispatch` call) would activate the legacy v1 `telegram` WASM channel alongside v2, despite the config-resolve and startup-time validators. Fix: - `ExtensionManager` gains a `reborn_telegram_v2_enabled: AtomicBool` field plus `set_reborn_telegram_v2_enabled(bool)` / `reborn_telegram_v2_enabled()` accessors. The host wires the flag at startup from `config.channels.reborn_telegram_v2_enabled` in `app.rs::init_extensions`. - `ExtensionManager::activate_wasm_channel` now consults the flag before any other work: when `true`, the legacy `telegram` channel is rejected with a typed `ExtensionError::ActivationFailed` whose message names `REBORN_TELEGRAM_V2_ENABLED` and issue #3285. - Canonicalize the input via `ironclaw_common::ExtensionName::new` inside the guard so non-canonical aliases (` telegram `, `tele-gram`) fail closed at the same shape activation accepts them. - Three new caller-level tests (`test_activate_wasm_channel_rejects_legacy_telegram_when_v2_enabled`, `..._rejects_legacy_telegram_with_whitespace_when_v2_enabled`, `..._allows_non_telegram_when_v2_enabled`) drive `activate_wasm_channel` directly and assert the v2 exclusivity message fires for canonical / non-canonical telegram aliases and does NOT fire for other channels. Copilot's adjacent finding on `src/config/channels.rs:503`: - The validator previously compared via raw `c == "telegram"`. Startup activation canonicalizes through `ExtensionName` (trims whitespace, folds hyphens), so non-canonical inputs would pass the validator but still bind as `telegram` at activation. Replaced the raw equality with `ExtensionName::new(c).map(|n| n.as_str() == "telegram")` so both the env-tier and the persisted-tier checks use the same canonicalization that `ExtensionManager` does. - Two new unit tests (`non_canonical_telegram_name_in_configured_list_still_blocks_v2`, `non_canonical_telegram_name_in_persisted_set_still_blocks_v2`) prove ` telegram ` is canonicalized and rejected on both axes. Verification: - 12 unit tests under `config::channels::telegram_v2_tests` pass (was 10 — +2 for canonicalization). - 5 `test_activate_wasm_channel_*` tests pass (was 2 — +3 for the v2 exclusivity guard). - `cargo test --test telegram_v2_default_off_integration` 10/10 pass. - `cargo fmt --all --check` clean. - `cargo clippy --workspace --all-features --tests` zero warnings.
|
Pushed @henrypark133 — hot-activation bypass (your CHANGES_REQUESTED finding)You're right — the config-resolve and startup-time validators don't cover the runtime path. A clean v2-only boot followed by Fix threads the flag through to the activation path itself:
Three new caller-level tests drive
@copilot — validator canonicalization (your inline on
|
henrypark133
left a comment
There was a problem hiding this comment.
Review: approve Telegram v2 default-off guard
The latest follow-up in 8a6531591 resolves my previous blocker. Runtime activation of the legacy telegram WASM channel now fails closed when REBORN_TELEGRAM_V2_ENABLED=true, and the flag is wired into ExtensionManager from app.rs.
What looks good:
- The config-time and startup-time validators now cover env-configured and persisted-active legacy Telegram channels.
- The activation path itself now rejects hot activation of legacy Telegram while v2 owns the Telegram webhook installation.
- The guard canonicalizes extension names before comparison, so non-canonical Telegram aliases do not bypass the check.
- The targeted config, activation-path, and integration tests pass locally, and CI is green.
Merge-order note:
…Config::resolve Two related changes addressing gemini-code-assist review comments on PR nearai#3356: 1. Wire `validate_telegram_v1_v2_exclusivity` into `ChannelsConfig::resolve`. The validator was exported but had no production caller — only its own unit tests invoked it. The intent (per CLAUDE.md and the issue body) was that startup would call it before binding the telegram webhook route. Moving the call inside `resolve` enforces the invariant eagerly during `Config::from_env` / `Config::from_db` rather than relying on a yet-to-be-written startup gate, and gives every consumer of `ChannelsConfig` the guarantee for free. 2. Change the validator signature from `(v1_active: bool, v2_active: bool)` to `(channels: &ChannelsConfig)`. The "v1 active" rule (wasm_channels enabled AND telegram listed in `configured_wasm_channels`) was documented in the docstring and left to the caller — a classic logic-drift seam. Encapsulating both axes inside the validator collapses them to one source of truth. Updates the 4 existing unit tests to construct minimal `ChannelsConfig` fixtures, adds 3 new cases covering the previously-uncovered partial-v1 states (telegram listed but wasm disabled; wasm enabled but telegram not listed) plus a `resolve`-end-to-end regression test that drives the full env→ConfigError path. Rewrites `tests/telegram_v2_default_off_integration.rs` against the new `&ChannelsConfig` shape. `ConfigError::InvalidValue { key, message }` is the right variant here and is preserved unchanged — there is no `MalformedConfig` variant in this codebase, despite the bot's suggestion. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Henry's review on PR nearai#3356 flagged a startup-bypass: the exclusivity guard ran only at `ChannelsConfig::resolve` time and only consulted the env-var view of v1 (`configured_wasm_channels`). But persisted `activated_channels` rows can carry `telegram` independently of `WASM_CHANNELS`, and `setup_wasm_channels` auto-loads persisted-active channels at startup. So a deployment with: - `REBORN_TELEGRAM_V2_ENABLED=true` - `WASM_CHANNELS` env var that does NOT list `telegram` - persisted `activated_channels` row that DOES list `telegram` …would pass the env-tier guard, then have both v1 (via persisted auto-activate) and v2 (via env flag) stand up for the same Telegram webhook installation. Fix: - `validate_telegram_v1_v2_exclusivity` gains an `Option<&HashSet<String>>` argument carrying the persisted-active WASM channel set. v1 is now considered active when `wasm_channels_enabled && (telegram listed in env OR telegram in persisted-active)`. The Option lets `ChannelsConfig::resolve` keep calling it (env-only) without inventing fake state. - `src/main.rs` re-runs the validator with `settings_persistence_available.then_some(&persisted_active_wasm_channels)` right before `setup_wasm_channels` fires. Fail-closed: a hit returns the same `ConfigError::InvalidValue` via `?` and aborts startup. - Three new unit tests in `config::channels::telegram_v2_tests` and three new caller-tier tests in `tests/telegram_v2_default_off_integration.rs` cover the new axis: persisted telegram + v2 blocks; persisted non-telegram + v2 allows; persisted telegram with `wasm_channels_enabled=false` allows (setup doesn't run, the set is dormant). Other reviewer findings addressed: - Copilot #1 (`tests/.../integration.rs:61`): replaced the brittle `Display`-substring assertion with a structured `match err { ConfigError::InvalidValue { key, .. } => ... }`. Wording changes to the message no longer break the test. - Copilot #2 (`channels.rs:582`): the unsafe `set_var` / `remove_var` pair in `resolve_rejects_v1_and_v2_telegram_together` clobbered any pre-existing developer-shell value. Replaced with a local `ScopedEnv` RAII guard that saves the previous value on `set` and restores it on drop (still gated by `lock_env()` for cross-test serialization).
Henry's CHANGES_REQUESTED review on PR nearai#3356 flagged that the startup exclusivity guard does not protect against runtime activation: a clean v2-only start followed by a `/api/extensions/telegram/activate` (or the equivalent `ToolDispatcher::dispatch` call) would activate the legacy v1 `telegram` WASM channel alongside v2, despite the config-resolve and startup-time validators. Fix: - `ExtensionManager` gains a `reborn_telegram_v2_enabled: AtomicBool` field plus `set_reborn_telegram_v2_enabled(bool)` / `reborn_telegram_v2_enabled()` accessors. The host wires the flag at startup from `config.channels.reborn_telegram_v2_enabled` in `app.rs::init_extensions`. - `ExtensionManager::activate_wasm_channel` now consults the flag before any other work: when `true`, the legacy `telegram` channel is rejected with a typed `ExtensionError::ActivationFailed` whose message names `REBORN_TELEGRAM_V2_ENABLED` and issue nearai#3285. - Canonicalize the input via `ironclaw_common::ExtensionName::new` inside the guard so non-canonical aliases (` telegram `, `tele-gram`) fail closed at the same shape activation accepts them. - Three new caller-level tests (`test_activate_wasm_channel_rejects_legacy_telegram_when_v2_enabled`, `..._rejects_legacy_telegram_with_whitespace_when_v2_enabled`, `..._allows_non_telegram_when_v2_enabled`) drive `activate_wasm_channel` directly and assert the v2 exclusivity message fires for canonical / non-canonical telegram aliases and does NOT fire for other channels. Copilot's adjacent finding on `src/config/channels.rs:503`: - The validator previously compared via raw `c == "telegram"`. Startup activation canonicalizes through `ExtensionName` (trims whitespace, folds hyphens), so non-canonical inputs would pass the validator but still bind as `telegram` at activation. Replaced the raw equality with `ExtensionName::new(c).map(|n| n.as_str() == "telegram")` so both the env-tier and the persisted-tier checks use the same canonicalization that `ExtensionManager` does. - Two new unit tests (`non_canonical_telegram_name_in_configured_list_still_blocks_v2`, `non_canonical_telegram_name_in_persisted_set_still_blocks_v2`) prove ` telegram ` is canonicalized and rejected on both axes. Verification: - 12 unit tests under `config::channels::telegram_v2_tests` pass (was 10 — +2 for canonicalization). - 5 `test_activate_wasm_channel_*` tests pass (was 2 — +3 for the v2 exclusivity guard). - `cargo test --test telegram_v2_default_off_integration` 10/10 pass. - `cargo fmt --all --check` clean. - `cargo clippy --workspace --all-features --tests` zero warnings.
…2-default-off-config feat(reborn): add telegram v2 default-off config guard
Split from #3316 (original PR by @nickpismenkov). This is PR 6/7 in the ProductAdapter stack.
Summary
REBORN_TELEGRAM_V2_ENABLED=falsedefault-off config marker.ChannelsConfig::reborn_telegram_v2_enabledplumbing.Stack
split/pr3316-05-telegram-v2-product-adapter.Authorship
Split commits retain
Co-authored-by: Nikolay Pismenkov <nickpismenkov@gmail.com>.Verification
cargo test --test telegram_v2_default_off_integration --offline --quiet— 5 tests passed.cargo test -p ironclaw config::channels::telegram_v2_tests --lib --offline --quiet— 4 tests passed.cargo fmt --all -- --checkand targeted ProductAdapter/Telegram/config tests.