Repository navigation
TRAE-011 — Production Architecture Closeout P0:Worker → Queue → Container → Neon 收口 #31
Description
总控编号
PASAY-TASK-011
执行者
TRAE
Route
dev
优先级
P0
任务名称
Production Architecture Closeout P0:把现有 Telegram Webhook / Cloudflare / Neon 基础收敛成唯一生产主链
Authoritative Target Architecture
冻结目标:
Telegram → HTTPS Webhook → Cloudflare Worker(fetch) → Cloudflare Queue → Cloudflare Worker(queue consumer) → Cloudflare Container(FastAPI/Pasay runtime) → Neon PostgreSQL → Telegram Reply
定时任务统一:
Cloudflare Cron / scheduled() → 同一 Queue → 同一 queue consumer → 同一 Container → Neon
说明:Cloudflare Queues 的消费者本身由 Worker queue() handler 承接,再通过 Cloudflare Containers 原生 binding 调用 Container;因此逻辑简图可继续写成 Queue → Container,实现层不得错误假设 Queue 能绕过 consumer Worker 直接调用 Container。
本任务完成后,上述链路就是 Pasay 唯一目标生产 runtime。
已有事实 / 必须复用
-
PASAY-WEBHOOK-ARCH-P0-001 / Issue #18 / PR #19已 merge:- FastAPI
POST /telegram/webhook - Telegram Update 持久化幂等
- PTB
Application.process_update(...) - bounded retry / stale claim recovery
- health / logging
- FastAPI
-
仓库已有 Cloudflare Worker + Neon/Postgres runtime boundary foundation。执行时必须读取当前 GitHub authority/base 的真实文件后复用;禁止重新造第二套
cloudflare-worker-v2、第二套 DB boundary、第二个 FastAPI app。 -
当前业务代码、Telegram UX、Membership / Property / Rent / Expense / Repair / Operation 都不是本任务重构对象。
-
TRAE 只负责开发实现、测试、commit/push、PR;不负责生产部署、账号配置、值守或 Ops。
Goal
一次性完成架构代码收口,使仓库具备可部署的唯一生产路径:
- 外部 Telegram 请求只进 Worker;
- Worker 不执行业务,只做边界校验 + enqueue;
- Queue 是异步缓冲与重试边界;
- queue consumer 只负责把标准 envelope 可靠投递到同一 Container;
- Container 内继续运行现有 FastAPI / Telegram handler / Pasay domain;
- 所有持久业务状态继续进入 Neon;
- Cron 与 Telegram 共用同一 queue/container/runtime,不再形成第二套执行栈。
禁止为了“架构漂亮”重写已经能工作的业务层。
Scope
A. Cloudflare Worker — Telegram ingress 只做最小入口
在现有 cloudflare-worker/ 原地增量:
- 暴露生产 Telegram webhook route(具体 pathname 复用当前配置,禁止再增加重复公开入口)。
- 校验 Telegram webhook secret / method / content type / payload 基础合法性。
- 解析最小必要字段:
update_id、可选chat_id,但不得在 Worker 内解释业务 intent。 - 将原始 Telegram Update 封装成 versioned queue envelope 后
env.<QUEUE>.send(...)。 - enqueue 成功后快速返回 Telegram 可接受的 2xx。
- enqueue 失败必须返回明确失败,不得伪造成功。
Worker fetch 层禁止:
- 初始化 PTB
- 调 FastAPI business service
- 直接写 Neon
- 调 LLM
- 复制 Rent/Repair/Expense 等领域逻辑
B. Queue Envelope — 只定义一个统一消息合同
建立最小 versioned envelope,至少:
versionkind:telegram_update | scheduled_jobevent_idoccurred_atpayload
Telegram:
event_id必须稳定映射update_id,保证重送可识别。
Scheduled:
event_id必须可确定性/幂等生成,禁止每次 retry 生成全新业务事件身份。
不要引入 Avro/Protobuf/schema registry;TypeScript type + Python Pydantic/schema(确有需要)即可。
C. Queue Consumer → Cloudflare Container
在同一 Cloudflare Worker 项目实现 queue(batch, env, ctx) consumer:
- 使用 Cloudflare Containers 当前官方原生 binding/API 调用 Pasay Container。
- 禁止新建公网“内部 API”再从 Queue 经 Internet 绕一圈。
- 每条消息投递到 Container 的单一内部 ingestion 边界。
- Telegram envelope 最终进入已存在的 FastAPI Telegram webhook/update processing 能力;优先抽出/复用内部 service,不复制处理逻辑。
- scheduled envelope 进入最小统一 job ingestion/dispatch 边界;若当前已有 scheduler/job service 必须复用。
Queue retry / ack 原则:
- Container 2xx 且明确 accepted/idempotent duplicate → ack。
- 临时失败 → retry。
- 永久 malformed / unsupported → 明确终结,不无限 retry。
- 使用 Cloudflare Queue 自带 retry/dead-letter 能力(如当前配置需要),禁止再造 Redis/Celery/RabbitMQ。
D. Container — 唯一 Python Runtime
现有 Dockerfile / FastAPI app 原地收敛,使同一 image 可作为 Cloudflare Container 运行:
- 一个 FastAPI app;
- 一个业务 domain;
- 一个 DB boundary;
- 一个 Telegram processing service;
- 不在 Container 内启动 PTB long polling;
- 不同时启动第二个 Telegram consumer;
- 不依赖 Windows 本机常驻进程才能工作。
容器启动必须 fail-fast:
- 必需 env 缺失 → 启动/health 明确 degraded/fail;
- migrations 连接使用 direct/unpooled URL(按现有 DB boundary);
- application runtime 使用现有 Neon 推荐连接方式;
- secret 不写日志。
E. Neon / Database Boundary 收口
复用现有 app/infrastructure/postgres.py / DATABASE_URL / DATABASE_URL_UNPOOLED 体系(以执行时代码事实为准):
- 不新增第二套 DB config。
- 不新增 SQLite/Redis 作为业务事实。
- webhook/update idempotency 与业务状态继续以 PostgreSQL/Neon 为事实源。
- Alembic 必须继续 single-head。
本任务除非 queue/container ingress 确有必要,禁止新增业务 migration。
F. Cron 统一进入 Queue
建立最小 scheduled() handler:
- 只负责生成合法
scheduled_jobenvelope 并 enqueue。 - 禁止 Cron handler 直接执行业务 DB 写入。
- 定时 reminder/digest/task wake-up 等后续业务都应可复用此入口,但本任务只建立基础通道,不新增运营功能。
G. Health / Architecture Truth
收口健康检查,至少能区分:
- Worker ingress alive
- queue binding configured
- Container runtime alive
- DB runtime boundary configured/connected(按现有 health 能力)
更新 CURRENT_ARCHITECTURE.md:
- 删除/明确标记 2026-08-10 long-polling/Hermes 拓扑为 historical,不再写成 current production target。
- 当前目标图只保留本 Issue 冻结的新架构。
- 明确 Telegram 与 Cron 共用 Queue + Container。
不要写长篇平台文档;只更新事实。
Long Polling / Legacy Runtime Exit Gate
代码层必须证明:
- Cloudflare Container 启动路径不会调用
run_polling()/getUpdates。 - 生产配置不要求 Windows 本机 PTB polling gateway。
- 不保留“Webhook 与 polling 同时都可以作为生产入口”的双主架构。
- 若仓库必须保留本地 polling 脚本用于 dev/debug,必须明确
development-only,默认生产关闭,且不得被 Docker/Cloudflare startup 引用。 - Hermes/Lily 不作为 Telegram transport/runtime;如相关代码仍存在,只能是独立 supervisor/AI capability,不得消费同一 bot token 的 updates。
本任务只做仓库代码/配置层收口,不负责到 Windows 机器停止服务或到 Telegram API 实际执行 setWebhook/deleteWebhook。
Required End-to-End Validation
必须有 targeted integration tests / deterministic harness 证明:
T1 Telegram ingress
合法 Update #123
→ Worker fetch
→ exactly one queue envelope
→ envelope.kind=telegram_update
→ event_id 稳定映射 #123。
T2 Duplicate Telegram delivery
相同 update_id 再次进入
→ 即使 Queue at-least-once / Worker 重送
→ Container/Postgres idempotency 仍保证关键业务 process exactly-once effect。
T3 Queue → Container
queue consumer 收到合法 telegram envelope
→ 通过原生 Container binding 调唯一 Pasay runtime
→ 成功 2xx → ack。
T4 Temporary container failure
→ Queue retry
→ 不丢消息
→ 不在 Worker 内自己循环无限 retry。
T5 Permanent malformed envelope
→ fail closed / terminal handling
→ 不进入业务 service
→ 不无限 retry。
T6 Scheduled
scheduled()
→ scheduled_job envelope
→ Queue
→ 同一 Container ingestion
→ 不走第二业务 runtime。
T7 No polling in production startup
静态 + runtime test 证明 Cloudflare/Docker production startup 不调用 Telegram long polling/getUpdates。
T8 Neon boundary
Container 使用现有 DB boundary;direct migration/app runtime 配置不串线;Alembic single-head。
T9 Regression
Issue #18 webhook/update service 直接相关测试继续 PASS;Telegram 业务 handler 不因 transport 改造发生语义回退。
Strictly Forbidden
- 不改 Telegram 3×2 一级菜单 / UX
- 不改 Owner / Secretary 产品逻辑
- 不做 Property / Rent / Expense / Repair / Operation 新功能
- 不做 Mini App
- 不改 OpenDesign
- 不引入 Redis / Kafka / RabbitMQ / Celery / Temporal
- 不建第二个 queue/orchestrator
- 不把 Worker 变成业务后端
- 不在 Worker 直接写 Neon
- 不重写 FastAPI
- 不新建第二个 Bot runtime
- 不做 Kubernetes / VPS / 新服务器架构
- 不 production deploy
- 不修改 Cloudflare/GitHub/Telegram account secrets
- 不处理 TRAE Auto Wake / ND 工作流
Stop Condition — ARCHITECTURE FROZEN
以下全部满足即立即 STOP:
- Worker fetch → Queue 已实现
- Queue consumer → Cloudflare Container 原生调用已实现
- Container → 现有 FastAPI/Pasay domain → Neon 已收敛
- Cron → 同一 Queue → 同一 Container 已建立
- Webhook/update 幂等继续成立
- production startup 无 long polling/getUpdates
- 无第二套 DB / Bot / business runtime
- health 可验证关键边界
CURRENT_ARCHITECTURE.md与代码一致- targeted tests PASS
- commit + push + PR
达到本 Stop Condition 后:
ARCHITECTURE_FROZEN = YES
后续除真实 blocker/production bug 外,禁止再开“继续优化架构”任务;主线转入功能修复与业务完善。
PR 创建后:
- 不等待 CI
- 不等待 CodeRabbit
- 不 merge
- 不设置
ready-for-owner - 输出
HANDOFF_COMPLETE
Final Report
中文精简报告必须包含:
PASAY-TASK-011- 执行者:TRAE
- Issue / PR
- branch / authority base SHA / head SHA
- changed files
- 最终实际 runtime diagram
- Worker ingress
- Queue envelope / retry / ack
- Container binding / ingestion
- Container startup 是否存在 polling(必须 NO)
- Neon DB boundary
- Cron 共用路径
- health
- targeted tests
- legacy runtime 保留/删除边界
- 明确未做事项
ARCHITECTURE_FROZEN = YES/NO- Final Status
最后一行:
HANDOFF_COMPLETE
Activity
fix(arch): PASAY-TASK-011 FIX1 — 4 P0 blockers for Issue #31
jhackuy commentedon Aug 20, 2026
PASAY-TASK-011-FIX2 已批准并进入返修。返修合同以 PR #32 最新
ND_RETURN — PASAY-TASK-011-FIX2评论为唯一执行范围;继续现有 branch/PR,禁止新建 PR、禁止 merge、禁止扩大 Scope。fix(issue31): ND_RETURN FIX2 containers, cron, worker tests, UTC val…
jhackuy commentedon Aug 20, 2026
PASAY-TASK-011-FIX8 — CI PostgreSQL 16 Final Repair
Executor: TRAE
Issue: #31
PR: #32
Branch:
issue/31-prod-arch-closeout-p0Task: 停止 SQLite 兼容路线,GitHub Actions targeted backend tests 改用 PostgreSQL 16。
Root cause
GitHub
pasay-gateRun #37 已确认:CI 仍使用DATABASE_URL=sqlite://,73 tests collected 后因 PostgreSQL-specific schema/constraints(当前首个明确错误为btrim())产生 22 errors。继续给tests/conftest.py增加 SQLite 兼容只会继续暴露更多 PostgreSQL 方言差异,不是正确修复方向。Required changes
.github/workflows/pr-ci.yml,为pasay-gatejob 增加 PostgreSQL 16 service:tests/conftest.py的 PostgreSQL test DB 隔离机制;不要再新增第二套 test DB 机制。tests/conftest.py双方言兼容分支,恢复 PostgreSQL-only test harness。aiosqlite只是 FIX5/FIX6 为错误 CI 路线新增,且生产/现有测试无真实依赖,则从requirements.txt删除。internal_ingest.py的 SQLite compatibility;如果仅为错误 CI 路线服务,则回退该 SQLite-only 分支。不要为了测试环境污染生产 PostgreSQL 路径。Required GitHub validation
必须由 GitHub Actions 真实执行:
tests/test_prod_arch_closeout_p0_031.pytests/test_telegram_webhook_p018.py硬验收:
本机 PASS 不能替代 GitHub CI。
Stop condition
完成 push 后提交 HANDOFF_COMPLETE;TRAE 不 merge。
Merge pull request #32 from jhackuy/issue/31-prod-arch-closeout-p0
ci(pasay-gate): expand backend pytest to M002/M1 Rent/Repair/Expense/…
Merge PR #47: PASAY V1 Mainline Convergence