diff --git a/docs/telegram-adapter-functional-analysis.md b/docs/telegram-adapter-functional-analysis.md new file mode 100644 index 0000000..6117640 --- /dev/null +++ b/docs/telegram-adapter-functional-analysis.md @@ -0,0 +1,194 @@ +# Telegram adapter: functional analysis and migration acceptance criteria + +- Status: proposed +- Technical workstream: CH-TG (Telegram compatibility adapter) +- Functional owner: Agis (Agent communications and platform agents) +- Operational delivery: Agda; contract ratification: Calluna and Agoga + +`CH-TG` is a technical-workstream identifier, not a platform module number. +Canonical `M1`–`M9` identifiers remain reserved by +`hyperdev-infra/docs/TEAM-RULES.md`; agent communications have no separate +canonical `M` identifier. +- System contract authority: [HyperDev unified channels contracts](https://github.com/gohyperdev/hyperdev-platform/blob/main/docs/architecture/unified-channels-contracts-and-delivery-plan.md) +- Decision record: [ADR-076](https://github.com/gohyperdev/hyperdev-platform/blob/main/docs/adr/ADR-076-unified-channels-edge.md) +- Runtime dependency: `hyperdev-channels` host runtime and Claude Code bridge + +## 1. Purpose and non-goals + +`hdcd-telegram` is the compatibility reference implementation for the Telegram +channel while HyperDev moves routing and configuration onto the platform. Its +current 0.2 router mode already provides a valuable migration primitive: one +Telegram poll owner, many Claude Code sessions and filesystem mailboxes. + +The target state is different: + +- the platform `channels-edge` owns the Telegram token reference, update offset, + provider authentication, access policy, conversation route and outbound API + delivery; +- the platform-managed host worker owns agent registration, mailbox receive and + session lease; +- Claude Code sees one generic `channels` MCP bridge, not a separate Telegram + MCP server; +- only agents with an explicit Telegram ChannelInstance are exposed through a + bot. The initial scope is Agda and Calluna. + +This repository does not become the source of tenant routing, agent discovery, +Teams support or provider-independent MCP lifecycle. + +## 2. Current capability inventory + +| Capability | Current `hdcd-telegram` | Target owner | +| --- | --- | --- | +| Bot API polling and single poller | standalone and `hdcd-router` | channels edge | +| Multi-session assignment | router topics + mailbox files | host worker session lease | +| Pairing and allowlist | local state | platform policy with migration import | +| Text, Markdown and chunked reply | local tool | Telegram adapter | +| Threads/forum topics | local tool/router | Telegram adapter + conversation route | +| Attachments/download | local tool | adapter/media policy | +| Reactions and edits | local tools | adapter capability operations | +| Permission relay | local inline keyboard | bridge/adapter extension path | +| Voice transcription | local Whisper path | explicit design decision: worker or media service | + +The existing direct binary remains a rollback path during migration. It must +not run concurrently with a platform poller for the same production token: +Telegram permits one `getUpdates` owner, otherwise updates conflict. + +## 3. Adapter contract binding + +The Telegram adapter implements provider contract C6. It consumes a verified +ChannelInstance and produces a source-neutral `CommunicationEnvelope`. + +### Inbound mapping + +```ts +type TelegramInboundMapping = { + provider: 'telegram'; + channelInstanceId: string; + providerUpdateId: string; + providerConversationRef: string; + senderRef: string; + text: string; + replyRef?: string; + attachmentRefs?: string[]; +}; +``` + +The adapter verifies bot/update provenance, deduplicates by update/message +identity, applies the configured access policy and asks the platform to bind +the verified channel instance to a tenant and target agent. Provider payload +cannot select an arbitrary tenant or agent. + +### Outbound mapping + +```ts +type TelegramDeliveryCommand = { + routeRef: string; // opaque platform conversation/contact route + operation: 'reply' | 'send' | 'react' | 'edit' | 'download'; + idempotencyKey: string; + payload: unknown; +}; +``` + +The adapter accepts only a CH-EDGE-issued route reference. A model, local worker or +Telegram user cannot supply a raw `chat_id` as an authoritative delivery target. + +## 4. Functional analysis + +### FA-TG-01: single-poller safety + +**Actor:** Telegram adapter deployment. + +**Behaviour:** acquire the configured bot token lease before polling; persist +the update offset; reject a second owner; release/expire the lease during +controlled cutover. + +**Acceptance:** the same production bot cannot be polled by legacy and platform +implementations at once. Shadow testing uses a distinct test bot. + +### FA-TG-02: inbound acceptance and pairing policy + +**Actor:** Telegram sender and channels edge. + +**Behaviour:** evaluate migrated pairing/allowlist policy before creating a +platform conversation/envelope; return the agreed pairing response where +required; store provider references tenant-scoped. + +**Acceptance:** unpaired sender cannot inject an envelope; a paired sender +cannot reach another tenant/agent; legacy access-state import is auditable and +reversible. + +### FA-TG-03: conversational reply and initiated send + +**Actor:** Claude Code agent. + +**Behaviour:** `reply` uses an opaque context; initiated `send` resolves only +an authorised contact/conversation alias. Telegram constraints remain visible: +the bot can start a message only where it already has an allowed chat route. + +**Acceptance:** threading follows the original message when appropriate; +unknown/raw chat targets are rejected; duplicate tool retries do not duplicate +an outbound Telegram message. + +### FA-TG-04: media and rich operations + +**Actor:** adapter and bridge. + +**Behaviour:** advertise operations only when the route/capability permits +them; process attachments through a tenant-scoped media policy; retain reaction, +edit and permission-relay semantics that are approved for the pilot. + +**Acceptance:** the feature-parity matrix below is tested for Agda and Calluna. +Voice transcription has an explicit owner before migration, rather than an +implicit loss of functionality. + +## 5. Definition of Ready + +- C4-C6 platform contracts are merged and fixture-backed. +- Telegram ChannelInstance schema, secret reference and access-policy ownership + are implemented by the platform workstream. +- Feature-parity scope for Agda and Calluna is signed off. +- A test bot is available for integration testing; a production cutover owner + and rollback procedure are named. +- Voice transcription disposition is decided: host worker capability or + platform media service. + +## 6. Definition of Done + +- platform adapter passes the approved text, thread, attachment, reaction, + edit, pairing and permission-relay matrix; +- one production token has exactly one poll owner; +- bot secret is absent from Claude Code MCP config, process arguments and logs; +- Agda, then Calluna, use the generic `channels` MCP bridge rather than a + separate Telegram MCP entry; +- agents without an enabled Telegram ChannelInstance have no Telegram route; +- rollback to the direct compatibility binary is documented and tested before + each production-token cutover. + +## 7. PR breakdown and integration plan + +| PR | Scope | Dependency | Acceptance | +| --- | --- | --- | --- | +| T1 | Telegram adapter contract fixtures and parity matrix | C4-C6 | mappings and errors are deterministic, no provider secret in fixture output | +| T2 | Compatibility bridge integration | CH-HOST/CH-MCP | one generic bridge can consume Telegram envelopes through test mailbox IPC | +| T3 | Platform adapter ingress/outbound | CH-EDGE | token lease, offset, dedupe and route-bound delivery pass integration tests | +| T4 | Agda pilot | T1-T3 | test bot then controlled production cutover with rollback evidence | +| T5 | Calluna pilot | T4 | same criteria on a second agent without cross-agent state leakage | + +Pre-production rollout uses the single platform runtime in controlled sequence: + +1. run fixtures and test-bot integration with T1-T3; +2. enable only Agda's test ChannelInstance; +3. stop legacy poller, enable platform poller and verify Agda production bot; +4. retain a time-bounded rollback window; +5. repeat for Calluna; +6. remove direct Telegram MCP configuration only after both pilots pass. + +## 8. Evidence required at each pilot gate + +- adapter version and configured ChannelInstance ID; +- token-poll owner and offset health, without token disclosure; +- inbound dedupe and outbound idempotency evidence; +- pairing/access positive and negative tests; +- reply, initiated send and attachment operation results; +- platform delivery/lease dashboard state; +- exact rollback command and named operator.