24 KiB
Abandoned and unused routes (Retired Routes)
This article only records three categories of things: ① routes that were evaluated but not adopted; ② documents that actually existed but were later deleted; ③ names that were once written into documents but never actually existed or were never implemented. The purpose is to retain the cause and prevent recurrence** - when you see these names in the future, you can immediately know what to use. See
docs/reference.mdfor the current real implementation and verifiable facts;docs/opengaps.mdfor gaps and blocking; seedocs/design.mdfor the current design. This article has no action items. If something becomes something to do, go todocs/opengaps.mdto register, and the entries in this article will be retained as a basis for decision-making. This article does not cite any non-existent paths: all paths point to files that actually exist in the warehouse; paths that have been written into documents but have never been implemented will only describe their form in text and will not be repeated verbatim to avoid being copied back into the text or viewed as real files. Verify baseline:b2dbe7c.
1. Route not taken
| Entry | Type | Reason | Alternative or Current Status |
|---|---|---|---|
teloxide (Telegram Bot framework) |
Not adopted | Telegram requires only a few API calls, does not introduce heavy frameworks (dptree/session middleware), and reduces dependencies and abstraction layers; reqwest has been introduced according to ARCH-DEPS-STAGE4 |
src/channel.rs is self-developed and implemented with reqwest |
teloxide planned feature set (macros / rustls / redis-session / throttle / webhooks-axum) |
Not implemented | Dead when teloxide is not adopted | — |
teloxide-core downgrade plan |
Not adopted | The premise for downgrading is "the use of teloxide is too heavy"; since the whole is not introduced, the premise for downgrading is not valid | — |
grammers / old telegram-bot crate |
Not adopted | Directly seal the Bot API + self-research to meet the needs | src/channel.rs |
BotError::Telegram(#[from] teloxide::errors::RequestError) |
Target form variant, not landed | Attached to teloxide, discarded with it | None |
BotError::Jmap / Storage / RateLimited / Unauthorized / Llm |
Target form variant, not yet implemented | It was designed to uniformly handle the five types of errors of JMAP/Redis/current limiting/authorization/LLM; now each module handles it by itself | Currently BotError only has Config / Io / Json / State Four variants (full text of src/error.rs) |
| Telegram server 30 msg/s current limit bucket | Target design, not implemented | No real Bot stress test data, no preset implementation; server bucketing is undertaken by TG API itself, and the bot side only performs backoff | 429 dedicated branch implemented (src/channel.rs: parse the response body parameters.retry_after and clamp ≤60s, the server-specified value will be used first, otherwise exponential backoff), so this line does not constitute a gap; the current processing of the server-side current limit bucket is still max_retries (default 3, hard upper limit 5) Universal retry |
Multi-step dialogue FSM (Idle / AwaitClarify / AwaitConfirm / Analyzing / AwaitFallback) |
Target design, not implemented | Current AI authorization only needs a Boolean + expiration time, multi-step is over-design | Redis TTL authorization state, key and TTL, see the AI authorization state section of docs/reference.md |
| Split each layer of domain / channel / notification / util into independent directories (sub-module split plan) | Target form directory, not implemented | The amount of code has not reached the scale that needs to be split | For the actual structure, see the project structure section of docs/design.md: the domain layer is src/domain.rs + src/domain/jmap/ (only client.rs); LLM is in src/ai.rs; notifications are in src/notify.rs; encryption and tool logic are inline in src/state.rs / src/config.rs, there is no independent util layer and no integration test directory |
| Teloxide style outbound message delivery integration test | Target form test, not yet implemented | Dependent on teloxide test mode | Unit test of src/channel.rs (#[test]) |
docker-compose healthcheck example (message-weave health --addr ...) |
Deleted example | src/main.rs has no CLI subcommand resolution, the subcommand does not exist, and copying will fail (in the early days, there was also a problem of not having curl in the image, and now curl is built in) |
/ready is detected by the platform ingress; for instructions, see docs/reference.md §9.4 (readiness detection) |
| Redis ACL password for SPA admin credentials = REDIS_URL | Route not taken | Obfuscating infrastructure credentials with UI admin password; .is_empty() guard for bootstrap_token when Redis has no ACL password (TLS-only managed Redis) makes SPA permanent 401 | Use instead CONFIG_ENCRYPTION_KEY (32-byte high-entropy hex required for startup, constant time comparison) |
1.1 Teloxide candidate comparison (evaluation record)
The following table and the following reasons are original evaluation conclusions, and retaining them is the basis for decision-making, not the current technical facts - "Active, latest 0.17, large download volume" and other framework attributes have not been reviewed in this round and do not represent the current version status of these crates.
| Framework | crate | maintenance status | features | fitness | conclusion |
|---|---|---|---|---|---|
| teloxide | teloxide |
Active, latest 0.17, large downloads | dptree distribution, conversational FSM, webhooks+webhooks-axum, Redis session storage, throttle, macros, tracing, rustls | ★★★★★ | Preferred |
| grammers | grammers / grammerslib |
General maintenance | MTProto (not Bot API), no Telegram Bot Token required | ★★ | Only if Bot API is not available |
| telegram-bot (old) | telegram-bot |
Basically discontinued | reqwest + futures | ★ | Not recommended |
6 reasons why I originally preferred teloxide:
- It is the same tokio + reqwest ecosystem as
jmap-client, and the runtime and TLS stack (rustls) can be reused. - Built-in Dispatcher +
UpdateKindenumeration matching commands, which is naturally compatible with command routing. - Support
webhooks-axum(production Webhook form; do not use long pollingNG-LONG-POLLING) and Redis session storage (remember the current folder/pagination cursor, without SQLite). - The
throttlefeature naturally fits Telegram’s 30 msg/s rate limit. - The
tracingfeature is consistent with the observation of this project. - The
macrosfeature can use#[teloxide::command]to automatically parse command parameters and reduce boilerplate.
Feature set plan at that time (when introduced in Phase 2): macros, redis-session, throttle, tracing, enable webhooks-axum on demand; rustls and rustls-native-roots on the TLS side Choose one of the two on demand (the latter supplements the OS root certificate for the former, which is convenient for local testing but unnecessary for production).
Replacement/Downgrade: If teloxide is upgraded or destructively changed, you can retreat to the thinner teloxide-core (retaining the core and types, removing the dispatcher abstraction); if you need multi-account high throughput, use webhooks-axum + shared axum::Router. Both of the above are no longer valid when teloxide is not adopted.
1.2 Dialogue FSM state transition table (target design, not yet implemented)
There is currently no FSM: parse_intent of src/worker.rs is directly parsed into Intent, and AI authorization is a Boolean expiration time in Redis. The following 5 states have been designed:
| Status | Meaning | Entering | Leaving |
|---|---|---|---|
Idle |
idle | any completion state | message received |
AwaitClarify |
The goal/intent is unknown, waiting for the user to choose | The intent or email target is not unique | The user gives a clear choice |
AwaitConfirm |
Wait for confirmation (AI analysis/attachment download) | User initiated analysis/download but not confirmed | Confirm/Cancel |
Analyzing |
AI request in-flight | User confirmation analysis | Success/Failure |
AwaitFallback |
AI failed, waiting for confirmation to fallback | 3 consecutive failed circuit breakers | User confirmation/cancel |
The invariants set at that time: AwaitConfirm / Analyzing / AwaitFallback involve AI or attachment downloading, ** LLM is not allowed to be called or attachments are not pulled until the confirmation state is reached **; the session state is short-term, and the external Redis short-term TTL is unified (SQLite is not used, and loss is acceptable). Channel neutrality requires that FSM status and events use domain types and do not rely on any channel SDK.
The invariant "AI analysis must first be explicitly authorized by the user" is still valid and has been retained in the session state machine section of docs/design.md; the state machine itself is not implemented.
2. Fictional entries (documentation has been written, code has never existed)
These names never exist in the code. Some of them appear in the directory tree of the target form, and some appear in unsubmitted drafts and discussion records. The common risk is that people mistakenly think that "this function has been implemented", so register them here to prevent their recurrence.
| Entry | Type | Reason | Alternative or Current Status |
|---|---|---|---|
| Directory scheme that splits Telegram access into four files "module entry/command parsing/session state/reply rendering" | Fictitious directory scheme | Drawn in the target directory tree, never created | Command parsing is in parse_intent (src/worker.rs); the session/authorization state is Redis TTL (the AI authorization state section of docs/reference.md); the rendering function is in src/channel.rs |
delivery:pending:{stream} |
Fictional key | Confused with Redis Streams' pending-entries list (PEL) - PEL is maintained internally by Redis and is not a writable key | Real key: delivery:inflight:{stream}:{id} (EX 60) and delivery:committed:{stream}:{id} (EX 604_800), see Delivery pipeline keys section of docs/reference.md |
check_config_reload |
Fictional function | Uncommitted name in draft, never made it into code | Hot updated to refresh_business_config (src/notify.rs:1173) |
push:registration:{sha256(callback_url)} was once written as "360s registration solo lock" |
Fact mislabeling | The key is 7d callback → subscription ID mapping | The real 360s solo lock is lock:push-register:{sha256(callback_url)} (src/notify.rs:1565-1566) |
MESSAGWEAVE_DOMAIN was once written as "Worker whitelist is invalid when production is not configured" |
Fictional assertion (unsubmitted draft/appeared in discussion, not documented) | This environment variable has zero hits in all positions; Worker whitelist is unconditional fail-closed: unknown path 404, method does not match 405, backend is missing or parsing failure 503 (cloudflare-worker/src/index.js:104-117) |
No need to configure switches, the whitelist will always take effect |
read_batch / retry_or_dlq "Redis may still return Ok(()) when an error occurs, so the consumption loop will not exit due to a single failure" |
Fictional assertion | The consumption entry is HTTP handler worker (notify.rs:433), not a background loop: full position src/ zero hit select!, no signal processing, no resident worker process. read_batch (state.rs:515) throws the Redis error as it is (state.rs:555) via ?, and the only discarded result is the BUSYGROUP idempotent protection of XGROUP CREATE (state.rs:523-531); retry_or_dlq is also thrown up via ?. The caller returns 503 service_unavailable + retryable=true for every Err, so the semantic premise of "the loop does not exit due to a single failure" itself is not true |
None - the gap does not exist; docs/opengaps.md (original name docs/roadmap.md) "Code Gap" 4 → 3, docs/design.md "Known Boundary" same sentence has been deleted |
| Build LLM capabilities into domain sub-module families (configuration/fallback/policy/audit layering) | Fictitious module tree | There are only jmap.rs and jmap/client.rs under domain/; LLM only has one file src/ai.rs (LlmClient) | None |
| ai::config / ai::fallback / ai::policy / ai::audit | Fictional module | LLM is configured in config.rs::LlmConfig, runtime parameters are in state.rs::OutboundConfig (delivered by RuntimeConfigProvider), no policy/audit concept | None |
| Build the channel layer into a directory module (including module entry file) | Fictitious directory | The channel layer is a file called src/channel.rs | None |
| notify::push_handler / notify::worker / notify::reconcile as module paths | Fictitious module paths | These are free functions within src/notify.rs (jmap_push / worker / reconcile), not module paths | None |
| mod_dedup / mod_streams / mod_sincestate | Fictional module name | state.rs / notify.rs are tile files, no submodules; deduplication and Streams logic exist as free functions | None |
| PushVerification type | Fictitious type | Undefined; register_push (notify.rs:1552) handles callback URL and verification code writeback inline | None |
| CancellationToken | fictitious type | unused; no graceful shutdown, no signal handling (src/ zero hits tokio::signal / ctrl_c) | None |
| Preview type / 4000 character long email protection / [Continue to view original text] button / /llm-fallback button | Fictional type and UI | All not implemented; after authorization, the full text will be handed over to LLM, and if it fails, the first 300 characters will be returned, without any truncation mark or button | None |
| LlmErr (5 variants) | Fictional enum | Real is AiError, only 3 variants (InvalidEndpoint / Request / Response) | None |
| LLM_TEMPERATURE / LLM_MAX_TOKENS / LLM_TIMEOUT_SECS / LLM_MAX_RETRIES environment variables | fictitious environment variables | src/ zero hits (LLM_MAX_RETRIES is the Redis running parameter max_retries, non-environment variable); there are 6 real business configuration fields: LLM_ENABLED / LLM_ALLOW_NET / LLM_API_KEY / LLM_BASE_URL / LLM_MODEL / LLM_SUMMARY_TARGET_CHARS (the latter value is hard-coded in the constructor and not entered into the wire), and there are two runtime parameters llm_timeout_ms / max_retries | See docs/reference.md AI Authorization section |
| llm.call tracing span | Fictitious observation point | Zero tracing events, zero span in src/ except main.rs (5 events); no logs for LLM calls | None |
| Circuit breaker / half-open state / 60s cooling after blowing / rule clearance (Redis share count) | No design implemented | Only timeout + retry; LLM failure will be silently downgraded to the first 300 words of fallback, no user-side prompts, no status records | None |
| Scheduled summary/daily email summary push | Not implemented | Not implemented | None |
| Attachment download (send_document / Blob/get / download button) | Not implemented | JMAP side read-only; email attachments only appear as has_attachment: bool | None |
| /flag / /unseen / Send command | Not implemented | Currently 6 intents recognized (Help/Consent/Summary/Search/General Messages/Unrecognized); of which /search is implemented (worker.rs:680) | None |
| Identity concept | Not implemented | Account identification only relies on ACCOUNT_ID, no identity layer abstraction | None |
| Extract RUN_MODE into a separate configuration file | Fictitious splitting scheme | RUN_MODE was once read by config.rs and verified in src/main.rs. This variable and the verification block have been deleted together (see §4); there has never been such a function as validate_env_or_exit | None |
| docker-compose message-weave health --addr example | fictitious command | Applies no CLI subcommand; health check endpoints are GET /healthz and GET /ready | See the Health-check section in docs/reference.md §9.4 |
| Early design debate questions (message format/long message threshold/attachment policy/Identity/monitoring/LLM provider/circuit breaker, etc. 17 items) | Answered by code | The answer is given by the code that has been implemented and is no longer a pending item | See the "Early questions answered by code" section of docs/design.md |
3. Deleted documents
| Entry | Type | Reason | Alternative or Current Status |
|---|---|---|---|
docs/todo.md |
Superseded (deleted) | Unclear structure, overlapping with design/deployment, and once carrying historical narratives such as "this round has been closed" | First replaced by docs/roadmap.md, which was renamed and narrowed to docs/opengaps.md on 2026-09-29 (only unfinished/unverified gaps, blockages and next-stage goals are included) |
| Temporary handover files in the root directory (no such files are left) | Temporary handover files, never entered the git history | Handover content should be returned to the permanent document, no temporary files in the root directory are left | Contents are merged into docs/design.md, docs/reference.md, docs/opengaps.md |
4. Deleted compatibility path (code used to exist, now deleted)
Unlike the previous two sections: the following items actually existed in the code and are neither fictional nor "unadopted". They are compatible with early
The parsing layer retained by the environment variable deployment form has been deleted entirely - no compatibility commitment is required in the early stages of the project, and deletion is cheaper than retaining.
After deletion, src/config.rs does not contain any std::env. Read: process startup only reads 2 credentials plus listening port, and other business fields
All use Redis business configuration (/api/bootstrap or administrator PUT → validate_nonblank fail-closed).
| Entry | Type | Reason for deletion | Current status |
|---|---|---|---|
Config::from_env() (line 311) of old src/config.rs |
Removed function | Full 20-item environment variable parser. After the credentials are migrated to the Redis business configuration, it becomes the only compatibility layer and there is no longer any caller | The startup period is changed to main.rs for direct reading: PORT(:31), REDIS_URL(:40), CONFIG_ENCRYPTION_KEY(:49), DEBUG_TOKEN(:83); business fields are carried by BusinessConfigWire |
required_secret / required_nonblank / env_bool |
Deleted helper function | Only called by the above parser, deleted together with it | Required semantics changed to validate_nonblank in the Redis business configuration path (called at 8 places) |
RUN_MODE variable + Config.run_mode field + startup verification block |
Deleted identifier | The two values never change any runtime behavior: webhook and reconcile share the same set of routing tables, POST /reconcile is an independent endpoint. This variable only creates an additional branch that must be interpreted by the documentation |
Zero hit across the board; NG-SERVER-MODE remains in docs/charter.md as a design non-target |
jmap_password() secure accessor |
Deleted method | Provides a non-disclosure read entry for the SecretString field, only meaningful in the env parsing path |
jmap_password is reserved as a business configuration field name (BusinessConfigWire / BusinessConfig), the field itself is SecretString |
Config.port / Config.redis_url field |
Deleted field | port is hard-coded 8080 in the two constructors, and the listening binding takes the value directly from the environment variable - the field value can be silently inconsistent with the real listening port, and the full position is zero-read; redis_url is also zero-read |
Config is narrowed to 7 fields: telegram / jmap / account_id / llm / auth / worker_token / timezone (the last item is mirrored from BusinessConfig for worker to render timestamp) |
redis_url parameters of two constructors |
Deleted parameters | The only reader (original line 72 of main.rs) was removed in the previous round of transformation | redis_only() and from_business(value); the wrappers from_business_json / from_business_value remove the first parameter synchronously |
| 19 legacy environment variable names | Read removed | See above; these names no longer have any readers in the code | See docs/reference.md §5.3 for list; see docs/design.md §7.1 for semantics |
Channel / Notifier / MessageAdapter (src/channel.rs) + UserCommand (src/domain.rs) |
Removed placeholder traits and types | The three no implementation, no caller, no dyn binding, the #[expect(dead_code)] attribute is the only source of reference; the actual Telegram outbound channel::telegram::TelegramClient is held directly by MetadataWorker and notify.rs of worker.rs and never passes through them. The only user of UserCommand is the deleted Channel |
channel.rs leaves only pub mod telegram (TelegramClient, reqwest self-developed); domain.rs leaves only the domain Notification and pub mod jmap module declaration (Notification is used in worker.rs:330); only reload_async remains in src/ 1 expect(dead_code) (overloaded API for testing) |
Note the difference between
LLM_MAX_RETRIESandLLM_SUMMARY_TARGET_CHARS: the former is never an environment variable (Redis running parametermax_retries, fallback to default, seedocs/reference.md§6.1), the latter is a constant (300 is hard-coded in the two constructors and has not entered the business configuration wire).
4.1 Business/management routes mounted in boot mode
router_configuration_setup originally directly entrusted the full factory router_with_worker_state_runtime_bootstrap_config,
Therefore, all 12 business and management routes are registered in the boot mode, but none of them work: the admin_token in the boot mode is empty,
And constant_time_eq is guarded with !expected.is_empty(). An empty expected value will permanently reject any candidate credentials; even if the authentication passes,
MemoryState also cannot persist a bootstrap write.
| Entry | Type | Reason for deletion | Current status |
|---|---|---|---|
/api/bootstrap, /api/admin/session, /api/admin/session/revoke, /api/config, /api/enabled, /api/bus iness-config, /webhook/tg, /push/jmap, /api/push/register, /api/push/disable, /reconcile, /worker |
Deleted route registration | Permanent 401 route reads like "wrong credentials" | Self-built AppState in boot mode, only /api/status, /ready, /healthz; the above path returns 404 |
The difference between 401 and 404 is not mysophobia: {"error":"unauthorized"} is completely the same as "wrong credentials". Operators and probes will
"These endpoints do not exist at all in this mode" was misread as "try again with different credentials". Now the two are distinguishable - /api/status still returns
{"missing":[...],"mode":"configuration-setup","ready":false} (HTTP 200), business and management planes directly 404.
Regression guarded by configuration_setup_mounts_only_status_and_probes: assert 12 paths 404 one by one, and assert
/healthz 200, /api/status 200, /ready 503.
5. How to restart one of them
- First register as a gap in
docs/opengaps.mdand attribute it to the stage. - If it involves Redis keys, TTL, HTTP routing or default values, update
docs/reference.mdin the same round. - The corresponding entry in this article is retained as the basis for decision-making - it records why we did not do this in the first place and do not delete it.