[5cab5a17] Relocate mount_telegram_miniapp_auth out of roboco/api/routes/telegram.py (#786)

* [5cab5a17] refactor(api): relocate mount_telegram_miniapp_auth from routes/telegram.py into app.py

Move the bare top-level helper (a conditional router mount + LoginRateLimiter
registration, not route-handler logic) out of roboco/api/routes/telegram.py
into roboco/api/app.py as private _mount_telegram_miniapp_auth, next to its
sole call site. This resolves pr_gate finding 276ae32f: classify_python.py
flags any non-@router-decorated top-level function as 'helper', which
.roboco/conventions.yml forbids under roboco/api/routes. The sibling
mount_cloud_auth already lives outside routes/ in roboco/api/auth/routes.py,
which is the same architectural precedent.

telegram.py: removed the function, the now-unused LoginRateLimiter import,
and the TYPE_CHECKING FastAPI block; updated docstring/comment references.
app.py: added _mount_telegram_miniapp_auth before create_app, imported
webapp_auth_router from routes.telegram and LoginRateLimiter from auth.login_limit.
test_telegram_webapp_auth.py: updated import and three call sites.

No route paths, schemas, or observable behavior changed.

* [5cab5a17] docs(map): reflect mount_telegram_miniapp_auth relocation into app.py

Update the agent-facing codebase map (docs/map/api-routes-schemas.md,
regenerated into _complete_map.md) for the placement-only refactor in
PR #786 / task 5cab5a17: mount_telegram_miniapp_auth moved out of
roboco/api/routes/telegram.py into roboco/api/app.py as private
_mount_telegram_miniapp_auth. Route-table row, Key Endpoints, Entry Points,
Config Flags, and the Changes-Since-Baseline note (Batch C trailing sentence
+ a new task 5cab5a17 entry) now point at the new location/name. No
route/schema/behavior change to document — placement only.

---------

Co-authored-by: Backend Developer 1 <be-dev-1@roboco.tech>
Co-authored-by: Backend Documenter <be-doc@roboco.tech>
This commit is contained in:
roboco-app[bot]
2026-08-01 18:54:08 +00:00
committed by GitHub
co-authored by Backend Developer 1 Backend Documenter
parent c317888aec
commit 70f059e5ff
5 changed files with 185 additions and 64 deletions
+6 -4
View File
@@ -39,7 +39,7 @@ The FastAPI surface of RoboCo: every HTTP route under `roboco/api/routes/` (the
| roboco/api/routes/docs.py | Project docs write/read/list/delete. |
| roboco/api/routes/x.py | X (Twitter) engine — CEO-only: list/approve/reject held draft posts + set/status OAuth 1.0a credentials. |
| roboco/api/routes/roadmap.py | Board roadmap engine — CEO-only: list open cycles + per-item approve/reject. |
| roboco/api/routes/telegram.py | Telegram credentials CRUD (CEO-only, write-only) + `webapp_auth_router` — a separate public, pre-auth `POST /webapp-auth` mounted only when `telegram_miniapp_enabled` AND `cloud_auth_enabled` are both armed (`mount_telegram_miniapp_auth`); validates a Mini App's `initData` and mints the cloud-auth session cookie; adds its own unconditional `LoginRateLimiter`. |
| roboco/api/routes/telegram.py | Telegram credentials CRUD (CEO-only, write-only) + `webapp_auth_router` (exported, conditionally mounted by `roboco/api/app.py`'s `_mount_telegram_miniapp_auth`) — a separate public, pre-auth `POST /webapp-auth` mounted only when `telegram_miniapp_enabled` AND `cloud_auth_enabled` are both armed; validates a Mini App's `initData` and mints the cloud-auth session cookie; adds its own unconditional `LoginRateLimiter`. |
| roboco/api/auth/ | Cloud auth (FastAPI Users, default off): `backend.py` (cookie transport + password-fingerprint-bound JWT strategy), `manager.py` (`UserManager` + DI chain), `session.py` (`resolve_session_user`, shared by the HTTP dual-path and the WS panel-token gate), `seed.py` (idempotent single seeded CEO login upsert), `routes.py` (always-public `/auth/status` + conditional login/logout mount), `login_limit.py` (`LoginRateLimiter` — per-IP POST rate limit, path-keyed via a `paths: tuple[str, ...]` set so `/login` and `telegram.py`'s `/webapp-auth` get independent buckets). |
| roboco/api/routes/v1/_role_dep.py | Per-role `Depends` bindings only (`require_dev`/`require_qa`/...) — the actual guard functions (`require_roles`, `require_authenticated_agent`, `envelope_to_response`) live in `roboco/api/deps.py` and are re-exported here (batch-A route-helper relocation, task `4baffaa3`). |
| roboco/api/routes/v1/do.py | Content verbs `/api/v1/do/*` (commit/note/say/dm/evidence/playbook...). |
@@ -78,7 +78,7 @@ The FastAPI surface of RoboCo: every HTTP route under `roboco/api/routes/` (the
| GET/POST | /api/telegram/credentials | telegram.py | `require_ceo_role` (agent context) |
| GET/POST/DELETE | /api/providers/presets, /presets/{id}/apply, /presets/{id} | provider.py | agent context — save/apply/delete a named full routing snapshot (`docs/map/support-services.md`) |
| GET/PUT/DELETE | /api/github-app/credentials ; GET /installations, /installations/{id}/repos | github_app.py | `require_ceo_role` (agent context) — App id + private key CRUD, installation/repo listing for the panel's repo picker |
| POST | /api/telegram/webapp-auth | telegram.py | public, pre-auth — Telegram `initData` HMAC validation; mounted only when `telegram_miniapp_enabled` AND `cloud_auth_enabled` |
| POST | /api/telegram/webapp-auth | telegram.py (`webapp_auth_router`) | public, pre-auth — Telegram `initData` HMAC validation; mounted only when `telegram_miniapp_enabled` AND `cloud_auth_enabled` (conditional mount in `roboco/api/app.py`'s `_mount_telegram_miniapp_auth`) |
| GET | /api/telegram/today | telegram.py | `require_ceo_role` (agent context) + 30/60s rate limit — Mini App V4's "Today" brief, backed by `TgCockpitService.today()` (one DB round trip, see `docs/map/notification.md`) |
| GET/POST | /api/auth/status (always), /auth/login, /auth/logout (mounted only when `cloud_auth_enabled`) | auth/routes.py | none (status) / FastAPI Users cookie login |
| GET/POST/PUT/DELETE | /api/projects, /{id}/conventions, /workspace, /sync | project.py | agent context |
@@ -222,13 +222,14 @@ roboco/api/
## Entry Points
- `roboco/api/app.py` `create_app()` builds the FastAPI app, mounts all routers under `/api` (prefix) + `/ws` (WS router).
- `roboco/api/app.py` `_mount_telegram_miniapp_auth(app, prefix)` conditionally mounts `telegram.py`'s `webapp_auth_router` (only when `telegram_miniapp_enabled` AND `cloud_auth_enabled`), mirroring `mount_cloud_auth` — it lives in `app.py`, not `routes/telegram.py`, because it is app-wiring (router mount + `LoginRateLimiter` registration), not route-handler logic (pr_gate `276ae32f`, task `5cab5a17`).
- `roboco/api/routes/v1/_role_dep.py` is imported by every flow router + do + a2a for HMAC/role guards and `envelope_to_response`.
- `roboco/api/routes/orchestrator.py` router constructed with `dependencies=[Depends(_require_ceo)]` (router-wide CEO gate).
## Config Flags
- Auth-gate mode: `_auth_required()` (env-driven; HMAC mandatory in prod-ish, optional in dev) — `api/deps.py`.
- Feature-flag routes are inert when their backing engine is off: `release.py` (ROBOCO_RELEASE_MANAGER_ENABLED), `prompter_live.py` MegaTask batch, `optimal.py` learnings (ROBOCO_ORG_MEMORY_ENABLED), `research.py` (ROBOCO_RESEARCH_ENABLED), `provider.py` grok/self-hosted (ROBOCO_GROK / self-hosted), CI-watch/dep-update originate elsewhere but surface via orchestrator/tasks.
- `telegram.py`'s `webapp_auth_router` doesn't merely no-op off — the route doesn't exist at all unless `telegram_miniapp_enabled` AND `cloud_auth_enabled` are both true (`mount_telegram_miniapp_auth`, called from `app.py`, mirrors `mount_cloud_auth`'s conditional mount).
- `telegram.py`'s `webapp_auth_router` doesn't merely no-op off — the route doesn't exist at all unless `telegram_miniapp_enabled` AND `cloud_auth_enabled` are both true (`_mount_telegram_miniapp_auth` in `roboco/api/app.py`, which imports `webapp_auth_router` from `routes.telegram`, mirrors `mount_cloud_auth`'s conditional mount).
## Gotchas
- `do` + `a2a` routers are token-only (any authenticated role), not role-asserted — any signed agent can call any content verb; service-layer scope is the only gate.
@@ -263,7 +264,8 @@ roboco/api/
> - ("panel-perf-p3-p4") adds `GET /api/dashboard/metrics/members` (batch scorecard fetch) — see `docs/map/metrics-observability.md`.
> - (task `4baffaa3`, "Batch A: extract route helpers") placement-only refactor, no route/schema/behavior change: moves every non-`@router`-decorated top-level helper out of `tasks.py`, `a2a.py`, `orchestrator.py`, `video.py`, `v1/_role_dep.py`, `roadmap.py`, `prompter_live.py` (`journals.py` had none) per `.roboco/conventions.yml`'s `no_helpers_in_routes` rule — DB/side-effecting logic to the paired `roboco/services/*` module, DTO-conversion helpers to the matching `roboco/api/schemas/*.py` (e.g. `task_to_response`), and small HTTP-layer auth guards (`envelope_to_response`, `require_orchestrator_ceo`, `validate_agent_id_param`, `require_ceo_role`, `require_pm_or_above`) into `roboco/api/deps.py`, replacing several route-files' redundant local `_require_ceo(agent)` wrappers with direct calls to the shared `deps.py` guard. Two real regressions surfaced during the relocation's revision rounds and were fixed before merge: `envelope_to_response`'s "verb rejected" structlog event was dropped in the move (restored — see the Key Symbols row above), and `_auth_required()` was narrowed to a truthy-only check that silently dropped its unset-value production fallback, which would have accepted unauthenticated `X-Agent-Role: ceo` header spoofing on an unconfigured production deploy (GHSA-4f7g-w95g-5q2c) — the three-branch fallback logic was restored.
> - (task `f8480831`, "Batch B: extract route helpers in remaining smaller-offender route files") placement-only refactor, no route/schema/behavior change: moved 28 non-`@router`-decorated helper functions out of 15 of the 24 batch-B route files (`optimal.py`, `project.py`, `release.py`, `dashboard.py`, `pitch.py`, `x.py`, `docs.py`, `git.py`, `playbooks.py`, `product.py`, `provider.py`, `research.py`, `secretary.py`, `system.py`, `work_session.py`) into their paired `roboco/services/*` module (DB/service-calling helpers), the route's own `roboco/api/schemas/*.py` as a converter (pure response/request shaping, mirroring `task_to_response`), or `roboco/utils/converters.py` (pure generic helpers); the other 9 files (`notifications.py`, `agents.py`, `cockpit.py`, `company_goals.py`, `kanban.py`, `secretary_live.py`, `settings.py`, `stream.py`, `usage.py`) had zero helpers by the precise `classify_python.py` classifier already. Added two small shared role-check helpers to `roboco/api/deps.py` (`require_auditor_or_ceo`, `require_role_in`) for endpoint-specific role gates that had no existing home.
> - (task `805e525a`, "Batch C: extract route helpers from the 11 Board-Program route files Batch A/B never touched", PR #785) placement-only refactor, no route/schema/behavior change: relocated the remaining 26 helper-kind top-level defs (pr_gate finding `276ae32f`) out of `board_programs.py`, `coroner.py`, `dogfood.py`, `github_app.py`, `mirror.py`, `periscope.py`, `pest_control.py`, `scales.py`, `sentinel.py`, `spackle.py`, `telegram.py` — each file's local `_require_ceo(agent)` wrapper is gone, every call site now calls `require_ceo_role` directly (same `action=` strings preserved); each file's `_status_value`/`_to_response` pair moved into its matching `roboco/api/schemas/*.py` module as `<name>_status_value`/`task_to_<name>_response`, mirroring `task_to_response`, except `board_programs.py`'s engine-backed `_to_response` (needs live DB reads) which became `BoardProgramEngine.to_response` in `roboco/services/board_programs.py`, mirroring the `release_proposal.py` precedent for an impure converter. `telegram.py`'s `mount_telegram_miniapp_auth` (app-setup, not request-handling) was left in place, unflagged by the conventions checker. 9 of the 11 route files (`board_programs.py`, `coroner.py`, `dogfood.py`, `mirror.py`, `periscope.py`, `pest_control.py`, `scales.py`, `sentinel.py`, `spackle.py` — the Board Program registry route surface, see CLAUDE.md's "Board Program registry" section) aren't yet itemized in this doc's Files/Key-Endpoints tables above; only `github_app.py`/`telegram.py` were already present. That backfill is still owed and out of scope for this placement-only pass.
> - (task `805e525a`, "Batch C: extract route helpers from the 11 Board-Program route files Batch A/B never touched", PR #785) placement-only refactor, no route/schema/behavior change: relocated the remaining 26 helper-kind top-level defs (pr_gate finding `276ae32f`) out of `board_programs.py`, `coroner.py`, `dogfood.py`, `github_app.py`, `mirror.py`, `periscope.py`, `pest_control.py`, `scales.py`, `sentinel.py`, `spackle.py`, `telegram.py` — each file's local `_require_ceo(agent)` wrapper is gone, every call site now calls `require_ceo_role` directly (same `action=` strings preserved); each file's `_status_value`/`_to_response` pair moved into its matching `roboco/api/schemas/*.py` module as `<name>_status_value`/`task_to_<name>_response`, mirroring `task_to_response`, except `board_programs.py`'s engine-backed `_to_response` (needs live DB reads) which became `BoardProgramEngine.to_response` in `roboco/services/board_programs.py`, mirroring the `release_proposal.py` precedent for an impure converter. `telegram.py`'s `mount_telegram_miniapp_auth` (app-setup, not request-handling) was left in place at the time (subsequently relocated by task `5cab5a17`, see below). 9 of the 11 route files (`board_programs.py`, `coroner.py`, `dogfood.py`, `mirror.py`, `periscope.py`, `pest_control.py`, `scales.py`, `sentinel.py`, `spackle.py` — the Board Program registry route surface, see CLAUDE.md's "Board Program registry" section) aren't yet itemized in this doc's Files/Key-Endpoints tables above; only `github_app.py`/`telegram.py` were already present. That backfill is still owed and out of scope for this placement-only pass.
> - (task `5cab5a17`, "Relocate mount_telegram_miniapp_auth out of roboco/api/routes/telegram.py", PR #786) placement-only refactor, no route/schema/behavior change: moved the last helper-kind top-level def flagged by pr_gate finding `276ae32f` — `mount_telegram_miniapp_auth` (a conditional `webapp_auth_router` mount + `LoginRateLimiter` registration, i.e. app-wiring, not route-handler logic) — out of `roboco/api/routes/telegram.py` into `roboco/api/app.py` as private `_mount_telegram_miniapp_auth`, inlined next to its sole call site (`create_app` → `_mount_telegram_miniapp_auth(app, f"{api_prefix}/telegram")`), mirroring `mount_cloud_auth` which already lives outside `routes/` in `roboco/api/auth/routes.py`. `app.py` now imports `webapp_auth_router` from `routes.telegram` and `LoginRateLimiter` from `auth.login_limit`; `telegram.py` drops the function, its `LoginRateLimiter` import, and the `TYPE_CHECKING` FastAPI block. After the move `telegram.py` has zero non-`@router`/`@webapp_auth_router`-decorated top-level functions (only route handlers remain); `make gate` passes with no new lint/mypy/xenon findings.
## Regression Risks