Instruction file imported from livezingy/DocuVision (
.cursor/rules/010-routing.mdc). Copyright stays with the author.
后端路由架构规范
生成自 kernel
docs/agent-ops/core/(routing)。勿手改副本;改共享约束请编辑 kernel 后重跑scripts/sync_agent_rules.py。
目标
把 2644 行的 main.py 收敛为 268 行装配层;路由全部进 backend/app/routers/,用机器可判规则锁死风格,使不同 Agent 后续新增路由时产出同构代码。S1 把同构纪律延伸到 services/** 的落点:新函数放哪个域、跨域调用怎么登记,不再靠 Agent 自觉。
硬规则(lint_routes.py 机器判,违反 = CI 红)
- 路由只进域 router:
@app.get|post|put|delete|patch|websocket装饰器只允许出现在backend/app/routers/*.py;main.py与services/出现即红。新路由写进对应域文件,必要时新建域。 router = APIRouter()且禁止prefix参数:路径写全量,include_router不重复挂前缀——保证 OpenAPI 路径不变。- 禁止 import
app.main:routers/、core/runtime.py、services/不得from app.main import ...(防环 + 重依赖);共享状态走core/runtime.py。
新增/删除路由的强制动作(防「门禁阻碍演进」)
- 登记清单:
backend/tests/test_route_inventory.py维护显式ROUTES表(域 → 路径列表);新增必须登记、删除必须注销,route 数守恒断言随之更新。 - 更新 OpenAPI 快照:新增/删除路由改变
app.openapi()→ 全量快照测试 additive 更新 baseline 并重新落盘(云测);未同步更新快照 = 红。上云前先用规则 6 在本机自查。 - 本机契约冻结(先于云测):
backend/tests/test_route_contract_freeze.py以 AST 冻结每条路由的函数名 / 签名 / 文档串 / 装饰器关键字(即 OpenAPI 可见面)。改动这四项须显式重生成:cd backend && DOCUVISION_ROUTE_FREEZE=write pytest tests/test_route_contract_freeze.py。纯搬移/重构(不改契约)不得触发重生成——v1.8.2 C2 的翻车点正是「顺手清理 docstring 乱码」导致description漂移,当时只有云端全量快照能抓。
services 层硬规则(S1,lint_services.py 机器判,经 audit_agent_ops.py 接线)
- S1a 层方向:
services/**禁 importorchestration/**、routers/**、core.runtime;orchestration/**禁 importrouters/**、core.runtime;routers 禁互引;core(除 runtime)与models/**禁 import 上三层;packages/docuvision-core禁 importapp.*。依赖律:main → routers → services → core/models/packages,orchestration 是 services 的同级消费方(DI 组装)。 - S1b 域归属 fail-closed:
services/**每个文件必须登记在scripts/service_domain_map.json(15 域,文档真源 =module-map.md§2b)——新文件未映射即红;跨域 import 必须在scripts/service_dependency_allowlist.json有登记条目(冻结棘轮只减不增,新增条目 = 红线先问,移除免费),或目标为已登记共享叶(shared_leaf_modules:L1 零出向机检 + L2/L3 人审证据,任何域可 import——I-1,前端 F5leaf_services的后端对应物)。新文件落域 / 跨域决策在交付 footer 写 Placement 判据 N/X(kerneltesting.md)。 - S1c 无环:域间依赖图非豁免边成环 = ERROR;环上边全在册 = 90 天时钟到期 WARN——整改方向是上提公共谓词到共享叶或倒置依赖,不是互相 import。
- S1 管什么/不管什么(防误读):管六层 import 方向、services 域归属、域间边与环;不管行数(S-a/b/c)、路由与契约(R1-R3/INV)、测试登记(T 系)、前端结构(F 系)、域内文件职责划分(归行数门禁 + 黄金样例 + Placement 人审)。
黄金样例(样例即真源)
- 新增路由先照
backend/app/routers/system.py(拆分后最小域)的结构抄:router = APIRouter()置顶 → 服务/依赖 import → 路由函数(response_model显式声明)。 - services 落点照抄三个样板:单域多内聚模块 =
table_*家族(识别/伪表过滤/对齐/回填/HTML/置信各司其职,同域互引);子包领域 =services/evidence/(owning doc + 子包边界);域内私有叶 =_layout_geometry.py(下划线前缀、纯函数、无状态)。 - 样例与本规范冲突时以样例为准;改样例须同步本规范。
本机 lint
python scripts/lint_routes.py(stdlib AST,不 import、不碰 paddle):校验规则 1/2/3。
python scripts/lint_services.py(同范式):校验 S1a/S1b/S1c + module-map.md §2b 三向对账;--selftest 为负向探针回归。
