Review: Add CORS middleware — frontend at pal-e-production hostname cannot fetch API
Verdict: NEEDS_REFINEMENT
Board item: #971 on
Forgejo issue: forgejo_admin/pal-e-api#256
Type: Bug (Lucas-authored)
Review date: 2026-04-11
board-pal-e-docs (backlog)Forgejo issue: forgejo_admin/pal-e-api#256
Type: Bug (Lucas-authored)
Review date: 2026-04-11
Template Completeness
Routed to
template-issue-bug. All required sections present.- [x] Type — Bug
- [x] Lineage — standalone, surfaced during westside-emails note creation
- [x] Repo —
forgejo_admin/pal-e-api - [x] What Broke — clear root cause with CORSMiddleware git-log evidence
- [x] Repro Steps — 5 steps including shell curl repro
- [x] Expected Behavior
- [x] Environment — cluster, ingresses, repos, frontend config var
- [x] Acceptance Criteria — 9 ACs, all testable
- [x] Related — arch-westside-emails, #217 rename, file:line refs
Traceability
- [x]
story:reader-browselabel — "Reader browses public notes, plans, and project pages in a web UI without authentication" - [x] story note verified — exists in
project-pal-e-docsuser-stories table (row 5). This bug directly gates that story: the new pal-e-production hostname is the reader-facing surface and it cannot fetch any data today. - [x]
arch:notes-apilabel — refers to the API-routes abstraction layer (routers registered inmain.py), distinct from thenotesentity component inarch-domain-pal-e-docs - [ ] arch note MISSING — no
arch-notes-apinote exists.arch-domain-pal-e-docsComponents table listsnotes(entity/table layer) but has no API-routes component entry. [SCOPE] Createarch-notes-apinote (or add a "notes-api" / "routes layer" row toarch-domain-pal-e-docsComponents table) documenting the FastAPI router layer. Non-blocking for fix — noted per skill. - [x] Forgejo issue — URL valid, issue open (created 2026-04-11T20:37:20Z)
- [x]
type:buglabel — matches issue type - [x]
scope:discoveredlabel — matches Lineage
File Targets
- [x]
src/pal_e_docs/main.py— verified. File exists. Line 49 is theFastAPI(...)instantiation as claimed. Routerinclude_routercalls are lines 51-58, so "add CORSMiddleware before router includes" is accurate (insert between line 49 and 51). - [x] CORSMiddleware absence verified —
Grep "CORSMiddleware"across~/pal-e-docsreturns zero files. Middleware has never existed in this repo, matching the ticket claim. - [x]
tests/directory exists withtest_health.py,conftest.py, and 17+ other test files — good home for a newtest_cors.pyor test addition to satisfy AC #7. - [x]
~/pal-e-app/src/lib/api-client.ts:16— not reviewed on filesystem (referenced only for context on whether credentialed requests are sent; dev agent should confirm to decideallow_credentials).
Repo Placement
Correct.
~/pal-e-docs is the local checkout of forgejo_admin/pal-e-api (confirmed via git remote -v). Fix is single-repo, single-file. No cross-repo split needed.Dependencies
Downstream blocker: Board item #972 ("Swap hostname routing — pal-e-docs serves frontend, api.pal-e-docs serves API",
forgejo_admin/pal-e-platform#278) depends on this ticket. Without CORS middleware, the hostname swap in #972 will either (a) leave the frontend broken again post-swap or (b) force the swap design to route API and frontend under the same hostname to avoid CORS entirely. The #972 design decision is coupled to whether CORS is fixed first.The issue body does not mention #972 in the Related section. [BODY] recommendation below.
Upstream blockers: none. No items in
in_progress touch main.py or CORS config.Acceptance Criteria
9 ACs, all testable by an agent after implementation:
- AC1-4: code-level (middleware added, env var driven, default origins, methods/headers) — verifiable by reading the diff
- AC5-6: runtime verification via curl with
Origin+ preflightOPTIONS— concrete commands, reproducible - AC7: new test in
tests/— concrete, tests/ exists, pytest infra in place - AC8: browser render check — validation step, not blocking merge but testable
- AC9: CI deploy via existing Woodpecker — no new infra
- AC10: no regression for non-browser callers — verifiable by existing test suite
One nuance: AC4 says
allow_credentials=True "only if the frontend sends credentialed requests." This is a conditional decision the dev agent must make by reading ~/pal-e-app/src/lib/api-client.ts. Acceptable — it's explicit and testable.Blast Radius
Low.
- Single file (
main.py), single middleware registration, purely additive. - No other FastAPI app in the pal-e-api repo (only one
main.py). - CORS headers on responses are ignored by non-browser clients (curl, SDK, MCP) — no regression risk for existing callers.
allow_originsis env-var gated (PAL_E_DOCS_CORS_ORIGINS), so non-prod environments can extend origins without code change — good pattern, matches the env-var approach already inconfig.py.- No sibling service with the same bug: pal-e-api is the only FastAPI service in this project. basketball-api and others are separate architectures and out of scope.
Decomposition Assessment
5-minute rule check:
- File targets: 1 (
main.py) + 1 test file addition. Well under the >3 files across >2 repos threshold. - Acceptance criteria count: 9. Above the soft ">5 ACs" threshold, but 6 of the 9 ACs are trivial verifications of a single middleware add (methods, headers, credentials, preflight response, regression). They are not distinct work units.
- Estimated agent work: 3-5 minutes — add middleware + env var + 1 config setting + 1 test + 1 curl verification. Single commit, single PR.
No decomposition needed. Fits cleanly in a single agent pass.
Recommendation
[BODY]Add #972 /forgejo_admin/pal-e-platform#278to the Related section with a note: "Blocks #972 — hostname swap design depends on whether CORS is fixed first." Non-blocking for fix, but improves traceability.[SCOPE]Create anarch-notes-apiarchitecture note (or add a "notes-api / API routes layer" row toarch-domain-pal-e-docsComponents table) so thearch:notes-apilabel has a backing entity. Currentarch-domain-pal-e-docsonly documents the data/entity layer (notes,blocks, etc.), not the FastAPI router layer. Non-blocking for this fix — dev can proceed — but the backing note gap should be filled as a separate backlog item by Ava.
Neither recommendation blocks dev dispatch. Both are documentation/traceability cleanups. If Ava chooses to move #971 straight to
todo after a minor [BODY] tweak and file the arch note as a separate issue, that's appropriate — the scope and fix plan are solid.Summary
Scope is sound. File target, line reference, git-log claim, and tests/ infra all verified. ACs are testable and concrete. Blast radius is minimal. Ticket is ready to implement once the two minor traceability gaps are noted — one line in the issue body and one new arch note tracked as a follow-up scope item.