# EnCoach — Work Report **Period covered:** Recent hardening & delivery sprint (Phases 0 → 3 of the improvement roadmap). **Repository of record:** `https://git.albousalh.com/yamen/full_encoach_platform` **Primary branch:** `main` (= current `v4` tip, commit `93c530ee`). This report summarizes what was built, fixed, and documented in the last few days. It's organized by phase so each block maps 1:1 to the roadmap items and the commits that shipped them. --- ## Table of contents - [Executive summary](#executive-summary) - [What shipped — by phase](#what-shipped--by-phase) - [Phase 0 — Platform safety & operations](#phase-0--platform-safety--operations) - [Phase 1 — Exam correctness & data provenance](#phase-1--exam-correctness--data-provenance) - [Phase 2 — Performance & observability](#phase-2--performance--observability) - [Phase 3 — Human-in-the-loop, compliance, i18n, a11y, CI](#phase-3--human-in-the-loop-compliance-i18n-a11y-ci) - [Branding update](#branding-update) - [Repository migration](#repository-migration) - [Commits landed in this sprint](#commits-landed-in-this-sprint) - [Verification results](#verification-results) - [Known gaps & deferred items](#known-gaps--deferred-items) - [Operator checklist for production](#operator-checklist-for-production) --- ## Executive summary | Area | Before | After | | -------------------------- | -------------------------------------------- | ----------------------------------------------------------------------- | | Duplicate exam models | Two `student.attempt` models in two modules | Single canonical `encoach.student.attempt` in `encoach_scoring` | | Auth on AI routes | Public in places | `@jwt_required` on every AI / coach endpoint | | Health check | None | `GET /api/health` + `GET /api/health/ready` (DB + LLM probes) | | LLM resilience | Hangs possible | `request_timeout=30s` on OpenAI calls | | Exam submit | Straight to graded | QualityChecker + IeltsValidator → `pending_review` gate | | RAG metadata | Missing entity/course/subject | Full provenance on every embedding + chunking for large passages | | Question provenance | Anonymous | `model`, `prompt_hash`, `log_id` stamped on every `encoach.question` | | LLM output validation | Raw JSON hope-for-the-best | Schema validation before DB insert | | Response envelope | Inconsistent across controllers | `{items,total,page,size}` everywhere | | Reports | Python loops over thousands of rows | SQL `read_group` aggregates | | Payments | Mocked Paymob | Real Accept flow + HMAC-SHA512 webhook verification | | JWT | Single access token only | Refresh tokens + revocation table | | Frontend bundle | Monolithic | `React.lazy` + Vite `manualChunks` (radix/charts/icons/forms split) | | TypeScript errors | 181 | 0 | | i18n | Hard-coded English | `i18next` + en/ar locales + RTL auto-switch + LanguageToggle | | Exam review | No human review step | `pending_review → publish` workflow + admin UI | | AI prompts | Buried in Python constants | `encoach.ai.prompt` versioned templates + admin editor + render preview | | Student feedback on AI | None | Thumbs up/down → `encoach.ai.feedback` + admin triage | | GDPR | None | `/api/gdpr/export` + `/api/gdpr/delete` + Privacy Center page | | Dark mode | Partial | Full `ThemeProvider` + toggle + chart tokens | | Accessibility | Dialog warnings | `DialogDescription` on every `DialogContent` | | CI | None | GitHub Actions: frontend (tsc, lint, build, Playwright) + backend Odoo | | Documentation | Scattered | §21 Hardening Release in `PROJECT_SUMMARY.md` + 4 ADRs | --- ## What shipped — by phase ### Phase 0 — Platform safety & operations Goal: make the platform *safe to run* before adding features. 1. **Merged duplicate exam models.** `encoach_scoring` is now the single home for `encoach.student.attempt` and `encoach.student.answer`; the stale copies in `encoach_exam_template` were deleted along with the duplicate `/api/exam/*` controllers. 2. **Added `@jwt_required` to every AI / coach route** so no AI endpoint is reachable without an authenticated token. 3. **Added health endpoints** - `GET /api/health` — liveness. - `GET /api/health/ready` — readiness (DB ping + LLM reachability). 4. **Hardened OpenAI calls** with `request_timeout=30s` to prevent hung workers on flaky provider responses. 5. **Gated `seed_demo_data.py` raw SQL** behind an explicit `ENCOACH_SEED_DEMO=1` env flag so a stray run in production cannot clobber real data. 6. **Fixed `docker-compose.yml`** and shipped `odoo-docker.conf` so the container-local run works out of the box. 7. **Published `/logo.svg`** as a deployable asset (later superseded by the project-manager PNG — see [Branding update](#branding-update)). 8. **Promoted a canonical `cefr_mapper`** to `encoach_ai.services.cefr_mapper` (no more two copies drifting). 9. **JWT cache TTL = 30s** with an invalidation hook on user mutations so role/entity changes propagate within half a minute. ### Phase 1 — Exam correctness & data provenance Goal: make every AI-generated item *trustworthy and traceable*. 1. **QualityChecker + IeltsValidator wired into exam submit.** Submitted attempts now pass through a validation layer and are parked in `pending_review` if anything fails IELTS rubric / quality rules, instead of being scored blind. 2. **Populated RAG metadata** on every `encoach.vector.embedding` record: `course_id`, `subject_id`, `entity_id`, and taxonomy tags. Queries can now be scoped per tenant / per skill. 3. **Chunking pipeline** for content > 2000 chars keeps embeddings within model limits and preserves passage locality. 4. **Provenance fields on `encoach.question`**: `ai_model`, `prompt_hash`, `log_id` — so any item can be traced back to the exact prompt + run that produced it. 5. **Schema validation of LLM output** before DB insert (jsonschema-style). Malformed JSON no longer reaches storage. 6. **Response envelope unified** to `{items, total, page, size}` across all controllers. 7. **Approval reject rollback** now uses a PG savepoint so a partial failure during rejection leaves no orphaned rows. 8. **Ticket notifications** fire on status and assignee change. 9. **`new_project/` retired.** `backend/` is declared canonical; `new_project/DEPRECATED.md` carries the redirect notice. ### Phase 2 — Performance & observability Goal: make the platform *fast and measurable*. 1. **Reports use SQL `read_group`** instead of Python loops. The admin Reports pages no longer scan tens of thousands of attempt rows in Python. 2. **`X-Request-ID` middleware + structured JSON logs** so a single request can be traced end-to-end across controllers. 3. **Prometheus-style counters** (basic in-process) exposed through the new `encoach_api` `openapi.py` controller, which also exports a live OpenAPI spec by scanning `@http.route` decorators at runtime. 4. **In-process caching** for hot reports and the AI narrative endpoint, with a Redis-ready seam. 5. **Paymob Accept integration** — the full 3-step flow (auth → order → payment key) plus an HMAC-SHA512-verified webhook endpoint that updates `encoach.paymob.order` atomically. Credentials come from `ir.config_parameter`. 6. **JWT refresh tokens** with a revocation table and auto-refresh on the frontend API client. 7. **Composite DB indexes** on the hottest report / ticket / attempt paths. 8. **React.lazy + Vite `manualChunks`** split `vendor-radix`, `vendor-charts`, `vendor-icons`, `vendor-forms`, `vendor-react`, and `vendor-query` into their own bundles. 9. **Fixed all 181 TypeScript errors.** `npx tsc --noEmit -p tsconfig.app.json` now passes cleanly. ### Phase 3 — Human-in-the-loop, compliance, i18n, a11y, CI Goal: make the platform *professional and defensible*. 1. **i18n.** `i18next` + language detector + en/ar locales + RTL auto-switch + `LanguageToggle` component wired into both role and admin layouts. 2. **GDPR compliance.** `/api/gdpr/export` returns a JSON snapshot of the caller's personal data. `/api/gdpr/delete` anonymizes PII, nulls logs, deactivates the account, creates an `encoach.gdpr.erasure.request` tombstone, and refuses admin self-erasure. A new `PrivacyCenter` page in the student / teacher / admin portals exposes both actions. 3. **Human-in-the-loop exam review.** `pending_review → publish` workflow, backed by a new `review_workflow.py` controller and two new admin pages: `ExamReviewQueue` and `ExamReviewDetail`. 4. **AI prompt management.** New `encoach.ai.prompt` model with versioning ("one active row per key"), REST endpoints for list / retrieve / create / activate / render-preview, and the `AIPromptEditor` admin page. 5. **Student AI feedback loop.** `encoach.ai.feedback` with upsert semantics per `(user, subject_type, subject_id)`, reusable `AIFeedbackButtons` component, and the `AIFeedbackTriage` admin page with status filters and resolve actions. 6. **Dark mode** fully wired: `ThemeProvider` from `next-themes`, `ThemeToggle`, and chart color tokens that follow the theme. 7. **Accessibility.** `DialogDescription` added to every `DialogContent`, fixing the Radix a11y warning across the app. 8. **CI scaffolding.** `.github/workflows/ci.yml` runs two jobs on every push: *frontend* (tsc → lint → build → Playwright smoke) and *backend* (Postgres 16 + `odoo:19 --test-enable --test-tags encoach_api`). 9. **Onboarding consolidated.** Single root `README.md` plus four ADRs: - ADR-0001 canonical directory layout - ADR-0002 JWT refresh token flow - ADR-0003 paginated response envelope - ADR-0004 RAG metadata + chunking 10. **Dead code removed.** `ExamPage` marketing, `Index`, `ProfilePage` import, and other legacy routes excised. --- ## Branding update The project-manager-supplied `EnCoach` logo (icon + wordmark + tagline "Unlock your potential with AI powered platform") now appears across: - **Login page** — shown at `h-36 w-auto` so the wordmark and tagline are legible. - **Student / teacher sidebar** — small rounded icon when collapsed, full wordmark at `h-14` when expanded. - **Admin sidebar** — same collapsed / expanded behavior. - **`og:image` meta** (social cards) — already pointed at `/logo-icon.png`, so it picks up the new asset automatically. The duplicated inline `EnCoach` text was removed from every sidebar since the wordmark is now baked into the image. Commit: `47d09a3c chore(branding): adopt project-manager EnCoach logo across login and sidebars`. --- ## Repository migration To consolidate the codebase into a single canonical repo for future work, the entire monorepo was migrated to: - **`https://git.albousalh.com/yamen/full_encoach_platform`** Migration steps performed: 1. Added the new repo as the `origin` remote. 2. Pushed the current `v4` tip as `main` (default branch). 3. Pushed all six local branches: `v4`, `v3`, `frontend-v3`, `full_stack_dev`, `feature/full-backend-v1`, `feature/generation-page-ai-workflows`. 4. Pushed all tags (none existed locally). 5. Rebound `v4` upstream to `origin/v4`. 6. **Removed all seven prior remotes** from `.git/config`: `backend-v3`, `frontend`, `frontend-v3`, `ip-origin`, `mirror-monorepo`, `origin-backend`, `origin-frontend`. The local checkout now has exactly one remote — `origin` → the new URL — so any future `git push` / `git pull` goes there with no further config. --- ## Commits landed in this sprint ``` 93c530ee chore(ci,docs): GitHub Actions, ADRs, README overhaul, §21 Hardening Release e70a2854 feat(frontend): Phase 2/3 hardening release dcf5ea69 feat(backend): Phase 2/3 hardening release 47d09a3c chore(branding): adopt project-manager EnCoach logo across login and sidebars c016a522 feat(reports): replace mock Reports pages with real backend aggregates d940db07 fix(config): align Configuration pages with platform logic 7f23127e chore(remotes,docs): rename remotes to match source-of-truth doctrine 7737f6de docs(summary): declare encoach_backend_v4 + encoach_frontend_v4 as repos of record ``` File-churn totals for the four hardening commits: | Commit | Files changed | Lines + | Lines − | | ----------------------- | ------------- | ------- | ------- | | branding (logo) | 4 | +42 | −18 | | backend hardening | 70 | +4 221 | −716 | | frontend hardening | 75 | +3 722 | −546 | | CI + docs + ADRs | 10 | +740 | −96 | --- ## Verification results Ran before each commit and again after the final commit: | Check | Result | | ------------------------------------------------------ | ----------------------------------------- | | `python -m compileall backend/custom_addons/encoach_*` | Pass, no syntax errors | | `npx tsc --noEmit -p tsconfig.app.json` | Pass (0 errors, down from 181) | | `npm run build` | Pass — 3.63 s, bundles split as expected | | `python -m compileall seed_demo_data.py` | Pass | | Playwright smoke (`test:e2e`) | 2 / 2 tests pass (login renders, root→/login) | Frontend bundle profile after `manualChunks`: ``` vendor-charts 422.80 kB │ gzip: 112.73 kB vendor-radix 318.60 kB │ gzip: 99.50 kB index (app shell) 219.02 kB │ gzip: 65.36 kB vendor-forms 88.31 kB │ gzip: 24.40 kB vendor-icons 53.77 kB │ gzip: 9.83 kB vendor-query 41.17 kB │ gzip: 12.23 kB vendor-react 23.33 kB │ gzip: 8.60 kB ``` --- ## Known gaps & deferred items These were intentionally **not** addressed in this sprint to keep scope bounded. Each has a clear next owner / next step. - **P1.2 — `ir.rule` entity isolation + drop blanket `sudo()`.** Requires a full `sudo()` audit across every module + coordinated frontend changes. Deferred to a follow-up ticket. - **AIFeedbackButtons inside `AiStudyCoach`.** The coach's response currently lacks the `ai_log_id` that the feedback model keys on. The reusable component ships, but wiring it into the coach is gated on a small service change. - **Redis cache backend.** The in-process cache ships; wiring it to Redis is a config + deployment-time step and was deferred. - **Paymob credentials.** `payment.paymob.api_key`, `payment.paymob.integration_id`, `payment.paymob.hmac_secret`, and `payment.paymob.iframe_id` must be set in `ir.config_parameter` before checkout works. Left unset on purpose — no secrets in git. - **Arabic translation coverage.** Core UI (nav, auth, privacy, feedback, theme, common) is translated; deeper exam-builder strings still fall through to English. --- ## Operator checklist for production Before promoting to production, an operator should: 1. **Set Paymob credentials** in `ir.config_parameter`: - `payment.paymob.api_key` - `payment.paymob.integration_id` - `payment.paymob.hmac_secret` - `payment.paymob.iframe_id` 2. **Set the OpenAI key** in `ir.config_parameter` as `openai.api_key` (or the matching env var on the container). 3. **Run migrations** for the new modules / models: `encoach.ai.prompt`, `encoach.ai.feedback`, `encoach.gdpr.erasure.request`, `encoach.paymob.order`, `encoach.jwt.token` (refresh revocation). 4. **Verify health** from outside the container: `curl https:///api/health` → 200, and `curl https:///api/health/ready` → 200 or 503 with structured reason. 5. **Pin the default-branch protection** on `git.albousalh.com/yamen/full_encoach_platform` (`main`). 6. **Schedule the CI workflow** — first run happens automatically on the next push. 7. **Smoke Playwright locally** one more time: `cd frontend && npm run test:e2e` (requires browsers via `npm run test:e2e:install`).