chore(ci,docs): GitHub Actions, ADRs, README overhaul, §21 Hardening Release
Some checks failed
CI / Frontend — lint + build + e2e (push) Has been cancelled
CI / Backend — Odoo HttpCase (push) Has been cancelled

- .github/workflows/ci.yml: two jobs — frontend (tsc --noEmit, lint, build,
  Playwright) and backend (Postgres 16 + odoo:19 --test-enable
  --test-tags encoach_api) — catches regressions before merge.
- docs/adr/: start an Architecture Decision Record trail with
  0001 canonical directory layout, 0002 JWT refresh flow,
  0003 paginated response envelope, 0004 RAG metadata + chunking.
- docs/PROJECT_SUMMARY.md §21 Hardening Release: full recap of the AI
  quality loop, compliance, Paymob, i18n, and CI work shipped in this
  drop, plus new DB tables, REST routes, frontend routes, verification
  results, and operator-facing configuration.
- README.md refreshed for the v4 split-repo doctrine and the new feature
  surface.
- new_project/DEPRECATED.md: formal retirement notice pointing at
  backend/ as the canonical tree.

Made-with: Cursor
This commit is contained in:
Yamen Ahmad
2026-04-19 14:16:47 +04:00
parent e70a2854f4
commit 93c530eef2
10 changed files with 744 additions and 100 deletions

View File

@@ -0,0 +1,60 @@
# ADR 0003: Canonical paginated response envelope
- **Status:** Accepted
- **Date:** 2026-04-09
- **Deciders:** Platform team, Frontend team
## Context
Different Odoo controllers returned paginated data in at least three shapes:
- `{ data: [...], total: N, page, limit }`
- `{ results: [...], count: N }`
- `[...]` (bare array, no totals)
The frontend grew defensive code paths to handle all three, and every new
endpoint risked inventing a fourth shape.
## Decision
Every list endpoint MUST return the canonical envelope produced by
`encoach_api.controllers.base.paginated_envelope`:
```json
{
"items": [ ],
"data": [ ],
"total": 123,
"page": 1,
"size": 20
}
```
- `items` is the canonical field name. New code reads from `items`.
- `data` mirrors `items` for backwards compatibility with older callers and
can be removed once every consumer migrates.
- `total` is the total number of matching records across all pages.
- `page` is 1-indexed.
- `size` is the requested page size (capped server-side).
On the frontend, `PaginatedResponse<T>` in `frontend/src/types/common.ts`
exposes both `items` and an optional `data`, and service methods
(`users.service.ts`, `lms.service.ts`, etc.) construct a clean
`PaginatedResponse` object from whatever the server returns so that UI code
never sees the legacy fields.
## Consequences
- Positive: frontend code is simpler and typesafe — read `items`, done.
- Positive: OpenAPI spec advertises one consistent shape across endpoints.
- Negative: one additional key (`data`) is duplicated in responses. Cheap
(same reference, no JSON bloat) and easy to delete later.
## Alternatives considered
- **Follow JSON:API's `{ data, meta, links }` convention.** Rejected as
overkill: we don't use HATEOAS, and the extra indirection would force a
rewrite of every existing consumer.
- **Return a bare array + pagination headers.** Rejected because Odoo's
controller helpers make setting custom headers awkward and because it hides
totals from curl/Postman users.