v1.3.34.2 -- Telegram channels: legacy-default migration + diagnostic CLI¶
A continuation of the Telegram-notifications fix chain. v1.3.34.1 changed the default template + parse_mode but did NOT migrate channels that were already created with the old default persisted into their template column or parse_mode config key. v1.3.34.2 ships that migration and adds an argos channel inspect CLI for operators who need to verify channel state without sqlite3 or panel-API auth.
Why¶
Operator dogfood after v1.3.34.1 reported the SAME error post-deploy:
Byte offset 21 is exactly where the closing * of *config_change* lands in the OLD MarkdownV2-default-rendered output: [INFO] *config_change* -- which proved the worker was still rendering the old body. PHASE 0 diagnostic confirmed the v1.3.34.1 binary was running (the new escapeHTML symbol was in the deployed binary), so the bug was elsewhere. PHASE 1 diagnostic via the new argos channel inspect revealed: the operator's pre-existing Telegram channel had the old MarkdownV2 default body persisted in its template column at create time. Render(ch.Template, ...) falls back to DefaultTemplate(...) only when the stored template is empty; a stored non-empty value (even if it's a literal copy of the old default) wins. v1.3.34.1's code change could only help NEW channels.
Same shape on the parse_mode config key: any value set at create time bypassed v1.3.34.1's empty-parse_mode-fallback logic.
This is strike #10 in the upstream-behaviour pattern, the first one not about a third-party API surface but about panel-internal state: when a default-shape value gets persisted at entity-create time, a later code change to the default does NOT automatically apply to existing rows. The fix has to clear those rows so the empty-fallback path can re-route them.
What ships¶
Boot-time auto-migration¶
backend/internal/notifications/migrate_legacy.go (new): MigrateLegacyTelegramChannels runs at panel boot, after schema migrations and before HTTP serving begins. For each type='telegram' row:
- If
templateis byte-equal to the new exported constantLegacyTelegramDefaultTemplate(the pre-v1.3.34.1 default literal), the column is set to''. - If
config.parse_mode == "MarkdownV2", the key is removed from the config JSON blob (the encryptedbot_tokenand other fields are preserved -- only the unencryptedparse_modekey is deleted).
Customised templates (one byte different from the legacy literal) are left untouched. The migration is idempotent: a second run on a clean DB touches zero rows. Boot logs a single INFO line:
notifications: legacy Telegram migration complete
channels_scanned=N templates_cleared=M parse_modes_cleared=K
Wired in backend/cmd/argos/main.go immediately before the VAPID-key bootstrap, so it runs on every panel start.
Seven new unit tests¶
backend/internal/notifications/migrate_legacy_test.go:
TestMigrateClearsExactMatchTemplate-- happy path.TestMigrateClearsParseModeMarkdownV2-- second surface.TestMigrateLeavesCustomisedTemplateAlone-- the safety guarantee:LegacyTelegramDefaultTemplate + " "is left untouched.TestMigrateLeavesParseModeHTMLAlone-- non-target parse_mode values are not re-mutated.TestMigrateIsIdempotent-- second run is a no-op.TestMigrateIgnoresNonTelegramChannels-- WHERE type='telegram' scope respected.TestMigrateBothSurfacesOnSameChannel-- worst-case row with both stale template AND pinned parse_mode counts as 2 surfaces touched.
All seven pass. Combined with the v1.3.34.1 tests, the notifications package now has 12 passing tests covering the HTML render path, escape functions, MarkdownV2 regression, sender form-body shape, error-message wrapping, and the auto-migration.
argos channel inspect CLI subcommand¶
backend/cmd/argos/cli_channel.go (new). Reusable diagnostic:
docker compose exec argos /argos channel inspect --type telegram
docker compose exec argos /argos channel inspect # all types
Prints id, name, enabled, rate_limit_per_minute, the JSON-quoted template (newlines visible as \n), and the config keys with secret fields replaced by ***REDACTED*** (using the same per-type secret-key set as notifications.secretFields()). For Telegram channels two diagnostic annotations are added:
template-state: one ofempty/LEGACY/customised, so the operator can verify whether the auto-migration would touch the row.parse_mode-state:unset/pinned to MarkdownV2/pinned to HTML/pinned to <custom>.
Same env contract as the other CLI subcommands: requires ARGOS_DB_PATH (or --db <path>).
Version-string bump¶
backend/cmd/argos/main.go argosVersion and frontend/package.json version both move from 1.3.33 to 1.3.34.2. v1.3.34 (doc-only) and v1.3.34.1 (code change but deliberately frozen) left a hole: argos --version reported 1.3.33 for all three releases, so the operator could not distinguish between an un-redeployed v1.3.34 panel and a freshly-rebuilt v1.3.34.1 panel. v1.3.34.2 closes that hole permanently. Future rule: never freeze argosVersion when Go source changes -- only freeze for tag-without-rebuild releases like v1.3.27.1 / v1.3.34.
Documentation¶
docs/features/notifications.md (telegram section):
- New "Auto-migration of pre-v1.3.34.1 channels (v1.3.34.2+)" subsection documenting the boot scan, the byte-exact match semantics, the boot-log line operators should see, and the manual UI-edit fallback for customised templates.
- New "Diagnosing channel state without sqlite3" subsection documenting the
argos channel inspectCLI subcommand with example output annotations.
scripts/check-no-personal-data.sh clean.
Smoke gate¶
The smoke for this release is operator-mediated against the production stack:
git pull,make sync-prod,make deploy-prod.- Confirm version bumped:
- Confirm migration ran on boot:
- Confirm channel state via the new CLI:
- Click "Send test" on the existing channel via the panel UI. Verify Telegram receives the test message with bold event-type visible, no 400 error, and the
notification_deliveriesaudit row carriesstatus='sent'. - (Recommended) Trigger a real
config_changeevent (e.g. edit any host) and verify the Telegram channel receives that too.
The unit-tested mock-server smoke (the seven migration tests plus the v1.3.34.1 sender tests) verifies the request-path shape; the operator-mediated smoke is the EFFECT gate.
The release is NOT tagged until step 5 confirms.
Files changed¶
backend/internal/notifications/templates.go(LegacyTelegramDefaultTemplateconstant added)backend/internal/notifications/migrate_legacy.go(new)backend/internal/notifications/migrate_legacy_test.go(new)backend/cmd/argos/main.go(migration call wired into boot;argosVersionbumped to1.3.34.2; help banner mentions the newchannel inspectsubcommand)backend/cmd/argos/cli_channel.go(new)frontend/package.json(versionbumped to1.3.34.2)docs/features/notifications.md(auto-migration + CLI subsections)docs/release-notes/v1.3.34.2.md(this file)CHANGELOG.md,mkdocs.yml
NOT changed: any frontend behaviour; any DB schema (the migration mutates rows but adds no columns); any smokes under scripts/smoke/; the v1.3.34.1 sender / template behaviour for new channels.
Upgrade¶
cd ~/argos-edge
git pull
make sync-prod
make deploy-prod # binary rebuild required (Go source
# changes in templates.go +
# migrate_legacy.go + cli_channel.go +
# main.go)
After deploy-prod finishes, run the smoke-gate steps above.
For operators with an existing Telegram channel that has been silently failing on every notification since v1.3.21: the auto-migration runs on the first boot of the new binary; from that moment forward the channel renders with the v1.3.34.1 HTML default and Telegram accepts the messages. No manual channel reconfiguration is required for byte-exact-default rows. Operators with customised MarkdownV2 templates need to edit the template field manually in the panel UI (clear it to adopt the new default, or update it to use HTML syntax with escapeHTML).
Tenth-strike entry in the upstream-behaviour pattern¶
The nine-strike pattern documented in memory/project_four_strike_upstream_pattern.md becomes ten with this release. The new strike's lesson:
When changing a default-shape value that gets persisted at entity-create time, ALWAYS pair the code change with a migration that detects + clears exact-match legacy values and leaves customised values alone.
This applies prospectively to any future change of: webhook default body_template, email subject_template, browser_push payload shape, rule throttle defaults, host TLS-mode defaults, or anything else the panel writes a "current default" into at row-create time.