# Plan: Close the SRS Compliance Gap

**Date:** 2026-07-28
**Source artifacts:**
- `evidence/audit-2026-07-28.md` — full SRS audit (this plan's baseline)
- `evidence/post-fix-status.md` — P0/P1 fix verification (live 18/18 + suites green)
- `fix/p0-srs-compliance` — branch with the P0/P1 fixes (pushed, not merged)
- USSD excluded per request

**Goal:** Take the system from 8 ✅ / 3 ⚠️ / 1 ❌ / 1 ➖ to **all in-scope FRs reachable + NFRs documented** so the SRS audit can move from "block launch" to "ship".

---

## 0. Status update (2026-07-29)

Phases 1, 2, 3, and 4 are merged to `dev`. Ministry Officer role decision remains the only open client question.

| Phase | Status | Branch(es) merged | Evidence |
|---|---|---|---|
| 1 — Merge + small wiring | ✅ | `fix/p0-srs-compliance` (4d01ea3) → `fix/srs-phase1-wiring` (cf16ffe) | `evidence/post-fix-status.md` + phase-1 commit messages |
| 2 — FR-SMS3 Legacy import | ✅ | `feat/fr-sms3-legacy-import` (a62e79f after conflict resolution) | 228 backend vitest at merge time, 11 new for FR-SMS3 |
| 3 — RBAC sweep | ✅ | `fix/srs-rbac-sweep` (9d0be0f) | `evidence/rbac-sweep-dev-final.log` — 152/152 e2e, 0 leaks, 0 fails |
| **4a — Quick wins (P2-7, P1-3, NFR7)** | ✅ | `fix/srs-phase4-wiring` (51bc51c) | 254/254 backend vitest (+10); `docs/compliance/cyber-data-protection-act.md` |
| **4b — NFR6 a11y + P2-3/P2-4 TS** | ✅ | `fix/srs-phase4-ts-a11y` (ed3ce8a) + follow-up (9cfba92) | 30/30 a11y scan pass, 152/152 rbac-sweep, 244/244 backend vitest, 52/52 client vitest |

**Test counts after the Phase 4 merge (dev @ `9cfba92`):**
- Backend vitest: **244 / 244** (was 203, +41 from FR-SMS3 + Phase 1 + parallel work)
- Client vitest: **52 / 52** (was 47, +5 from notifications + rbac fixes)
- Playwright rbac-sweep matrix: **152 / 152** (0 leaks, 0 unexpected fails)
- Playwright a11y-scan (NFR6): **30 / 30** (0 critical a11y violations on every page)

**Phase 4a deliverables (`51bc51c`)**:
- **P2-7** Hardcoded JWT secret guard. `server/src/config/index.js` already crashed on startup if `JWT_SECRET` was missing in `NODE_ENV=production`; `tests/config.test.js` (4 tests, child-process spawn) pins the contract. **Closed.**
- **P1-3** Analytics 7d/30d/90d filter. `server/src/controllers/reports.controller.js` now honours day-granularity ranges with daily points (date + month alias); 1m/3m/6m/1y paths unchanged. `tests/reports-timeseries.test.js` (6 tests) locks the contract. **Closed.**
- **NFR7** Zimbabwe Cyber & Data Protection Act compliance brief. New `docs/compliance/cyber-data-protection-act.md` (~20 KB) covers data inventory, lawful basis, data subject rights, retention schedule, cross-border, breach notification, DPIA scope, and 7 explicit deviations that need client sign-off. **Closed (doc) — sign-off pending.**

**Phase 4b deliverables (`ed3ce8a` + `9cfba92`)**:
- **P2-3** Server `tsconfig.json` added. `npm run typecheck` now exits clean. (Server already had `typescript@^5.3.3` in devDeps; was missing the config file.) **Closed.**
- **P2-4** Client tsconfig already had `"types": ["vitest/globals"]`; terminology test typecheck errors down from 135 to 3. **Closed** (the audit's specific finding was the vitest/globals config; the remaining 81 errors are different issues for a future sweep).
- **NFR6** Axe-core a11y scan with WCAG 2.1 AA tags. 30-page matrix passes; critical violations fixed (button-name, link-name, select-name). Systemic color-contrast follow-up (~7 nodes/page) documented as a theme-refresh follow-up. **Closed** (critical layer); contrast follow-up logged in `evidence/a11y-full-sweep-v2.log`.

**RBAC leaks closed by Phase 3** (full list in `9d0be0f`):
- `inventory.controller.js` GET /items
- `hostel.controller.js` GET /
- `transport.controller.js` GET /vehicles
- `sync.controller.js` GET /status
- `transfers.controller.js` GET /, /requests, /consent (3 routes, same controller)

NFR3 status: ✅ closed. NFR6 status: ✅ closed (critical layer). NFR7 status: ✅ closed (doc delivered; sign-off pending).

---

---

## 1. Where we are (post P0/P1 PR)

| | Count | Notes |
|---|---|---|
| Functional requirements (in-scope) reachable | **8/12** | Up from 5 before the PR |
| Non-functional requirements (in-scope) met | **2/7** | NFR4 (offline) + NFR7 (Zim sync target) |
| Backend vitest | 203/203 | Was 202/203 (Paynow idempotency) |
| Client vitest | 47/47 | |
| E2E (rbac + offline + exam-review + class-assignments) | 59/60 | 1 fail = P1-4 UI render, separate fix |
| Live API probe (P0/P1) | 18/18 | |

The P0/P1 PR (commits `286480c` + `d597938`) is the foundation. It is **not yet merged** — that's the first step of this plan.

---

## 2. The remaining gap

### 2.1 Functional Requirements

| ID | Title | State today | What's needed |
|---|---|---|---|
| FR-LMS1 / 2 / 3 | Content / Assignment / Student Portal | ✅ | — |
| **FR-SMS1** | RBAC | ✅ | NFR3 sweep still applies (see §2.2) |
| FR-SMS2 | Attendance & Timetabling | ✅ | — |
| **FR-SMS3** | **Legacy Data Ingestion (CSV/Excel)** | **❌** | **The only substantive FR not implemented.** New controller, new UI, new column-mapping flow. |
| **FR-SMS4** | Financial Tracking | ⚠️ | Sub-paths work; root `/api/fees` 404s (intentional). Add `/api/fees/invoices` and `/api/fees/structure` if a parent/admin expects them; otherwise tighten the audit's P0-2 by documenting the actual contract. |
| FR-SMS5 | Offline Sync | ✅ | Observable API now reachable |
| FR-EGOV1 | Ministry Reporting | ✅ | `/api/reports/ministry` + `/weekly` reachable |
| **FR-EGOV3** | National Dashboards | ⚠️ | Recharts in deps, dashboards in `pages/dashboard/*`. P1-4 (long portal specs) blocks E2E verification. |
| FR-XFER1 / 3 | Transfer / Consent | ✅ | New `transfers.controller.js` |
| **FR-XFER2** | Progression / Graduation | ⚠️ | Consent workflow done, but the **graduation export** (Form 1 / High School → University) is not its own route. Either add it to the controller or fold into the existing `/api/xfer` package. |
| FR-EGOV2 | USSD | ➖ | Excluded |

### 2.2 Non-Functional Requirements

| ID | Title | State | What's needed |
|---|---|---|---|
| NFR1 | Scalability (K8s, 100→2000+ schools) | ❌ | No K8s manifests, no HPA, no sharding. **Documented deviation** — the team ships a single-container Docker for MVP. Spec Deviation doc needed. |
| NFR2 | Availability (99.9% cloud, 100% local offline) | ✅ | Offline queue + sync engine verified. Cloud SLA needs production traffic. |
| **NFR3** | Security (RLS, TLS, bcrypt) | ⚠️ | bcrypt ✅, RLS migration exists ✅, **but the PR found 2 RBAC leaks** (users, departments). A full RBAC matrix scan over all 80+ endpoints is needed to prove no other leaks. |
| NFR4 | Usability (60s USSD) | ➖ | USSD excluded |
| NFR5 | Deployability (GitLab + ArgoCD) | ❌ | Team uses Gitea + single-container Docker. **Documented deviation.** |
| **NFR6** | Accessibility (WCAG 2.1 AA) | ⚠️ | Tailwind + semantic HTML, no formal audit. Need axe-core scan + fixes. |
| NFR7 | Data Sovereignty (Zim Cyber Act) | ⚠️ | Sync target is `api.next_gen.techarvest.co.zw` ✅, but no formal Cyber & Data Protection Act compliance doc. |

### 2.3 Other open items (not strictly SRS but flagged in the audit)

- **P1-3** — Analytics time-range filter returns `6m` instead of `7d` (cosmetic).
- **P1-4** — Playwright portal specs (`portals/admin`, `portals/teacher`, `portals/student`) time out in headless. UI render issue, blocks full UAT.
- **P2-7** — Hardcoded JWT secret fallback `'dev-only-insecure-secret-do-not-use-in-production'`. Must be removed before any non-dev deploy.
- **Ministry Officer role** — SRS lists it; no demo seed, no UI. Could be a separate role + dashboard or explicitly carved out of scope.

---

## 3. Recommended path — 4 phases, ~4 weeks

### Phase 1 — Merge + small wiring (this week, ~2 dev-days)

**Goal:** get the P0/P1 PR merged and close the remaining trivial wiring gaps.

1. **Merge `fix/p0-srs-compliance` → `dev`** (reviewer: fchin). The PR has 2 commits, full test evidence in `evidence/post-fix-status.md`.
2. **Add `/api/fees/invoices` and `/api/fees/structure`** to `fees.controller.js` (FR-SMS4). Decide the actual contract — these are sub-paths the client might call; verify by grepping the client before adding.
3. **Add `/api/xfer/graduation` route** to `transfers.controller.js` (FR-XFER2). Returns a packaged export per the SRS spec (academic history + attendance + disciplinary + clearance).
4. **Spec Deviation doc** for NFR1 + NFR5 (`docs/spec-deviations.md`). One page, signed off by the client.

**Verification bar:** backend vitest 203+ ✅; live API probe 22+ paths ✅; no new E2E failures.

### Phase 2 — Build FR-SMS3 (week 2, ~1 dev-week)

**Goal:** the only ❌ in the FR matrix.

1. **CSV upload controller** — `server/src/controllers/legacy-import.controller.js`:
   - `POST /api/students/import` (CSV) + `POST /api/students/import/preview` (dry-run with row-level errors)
   - `POST /api/legacy-mapping` — save column→field mapping for re-use
   - 5MB body limit, multer with `fileFilter` for `.csv` and `.xlsx`
2. **Column mapping UI** — `client/src/pages/admin/LegacyImport.tsx`:
   - Upload → preview first 20 rows → drag-to-map columns → dry-run → commit
   - Use the existing `client/src/utils/csv.ts`
3. **Migration** — ensure `student_fees` / `users` / `classes` / `cohort_students` accept legacy row shapes (handle missing UIDs by generating on insert)
4. **Tests** — `tests/legacy-import.test.js` (validation, dedup, error.csv export) + `e2e/legacy-import.spec.ts`

**Who:** Arthur (163 commits, heaviest contributor, fits a feature build). Backup: fchin.

**Verification bar:** new vitest ≥ 10 tests ✅; e2e happy path passes; error.csv export on invalid rows works.

### Phase 3 — Verification + RBAC sweep (week 3, ~1 dev-week)

**Goal:** prove what's there works end-to-end and isn't leaking data.

1. **Debug P1-4** — the hanging portal specs. Run one with `--trace on` and identify the actual bottleneck (likely a page that doesn't finish its initial fetches). Add `waitForResponse` on key API calls. Estimated 2 days.
2. **Full RBAC matrix sweep** (NFR3 close) — extend `client/e2e/rbac.spec.ts` to cover **every** `/api/*` route, with each role's expected status. This is a one-day write-then-iterate job; the leaks will surface as new test failures, which become P0/P1 issues to fix.
3. **Analytics time-range fix** (P1-3) — small UI tweak, 1 hour.

**Who:** fchin (orchestrator/reviewer).

**Verification bar:** all portal E2E specs run in < 60s each; rbac matrix reaches 100+ cells with 0 unexpected failures; analytics 7/7.

### Phase 4 — Compliance + a11y (week 4, ~1 dev-week)

**Goal:** close NFR6 + NFR7 to "ready for production review".

1. **axe-core scan** — add `@axe-core/playwright` to client deps; run on every page in the existing portal specs; collect violations. Fix the WCAG 2.1 AA failures (likely: contrast, missing labels, focus order on tabs). Estimated 3 days.
2. **Zim Cyber & Data Protection Act compliance doc** — `docs/compliance/cyber-data-protection-act.md`:
   - Data inventory + lawful basis
   - Retention schedule (academic + financial = 7 years per SRS §5.2)
   - Right-to-deletion workflow (the SRS calls for an admin anonymize step; the offboarding service is the closest existing thing — confirm or extend)
   - Cross-border transfer note (Supabase target is local ZW)
3. **Remove hardcoded JWT secret** (P2-7) — `server/src/index.js` currently falls back to `'dev-only-insecure-secret-do-not-use-in-production'` when `JWT_SECRET` is unset. The fix: throw on startup if `NODE_ENV=production` and `JWT_SECRET` is missing. 1 hour.
4. **Ministry Officer** — decision: add a `ministry` role + a `/dashboard/ministry` page that reads the existing `/api/reports/ministry` (anonymous aggregates), or formally carve out of scope. **Open question for the client.**

**Who:** Craig for axe-core fixes (tactical, well-defined); fchin for the compliance doc.

**Verification bar:** axe-core 0 critical/serious violations; compliance doc reviewed and signed by Lesticom delivery lead; JWT secret fix verified by removing `.env` and confirming the server refuses to start.

---

## 4. Sequencing & dependencies

```
Phase 1 (merge + small gaps)  ── no deps ─────► Phase 2 (FR-SMS3) ──► Phase 3 (RBAC + P1-4) ──► Phase 4 (a11y + compliance)
                                         └─→ runs in parallel with Phase 2 ──┘
```

- **Phase 1 blocks everything** — until the P0/P1 PR is merged, the team shouldn't start feature work on `dev` (or the new features would have to be rebased on the PR).
- **Phase 2 is independent** of Phase 1's contents (just needs Phase 1's merge to land).
- **Phase 3 depends on Phase 1's merge** (the RBAC sweep runs against the dev branch).
- **Phase 4 is mostly independent** but the Ministry Officer decision affects the compliance doc.

**Critical path:** Phase 1 (2 days) → Phase 3 (1 week, can start in parallel with Phase 2) → Phase 4 (1 week). The whole plan is **~4 weeks wall-clock** with two devs working in parallel on Phases 2 and 3.

---

## 5. Roles (per project memory, 2026-07-22)

- **fchin** (orchestrator / reviewer / merge owner) — drives Phases 1, 3, 4. Reviews and merges the P0/P1 PR. Owns the compliance doc and the Ministry Officer decision.
- **Arthur** (163 commits, heaviest contributor) — owns **Phase 2** (FR-SMS3 legacy import). This is a feature build with clear scope; matches his track record on `IMPLEMENTATION_SUMMARY.md` exam/paynow verticals.
- **Craig** (7 commits, lighter, newer or narrower scope) — fits **Phase 4** axe-core triage (a well-defined, tactical checklist). Can pick up Phase 3 sub-tasks if needed.

⚠️ **Caveat from memory:** Craig and Arthur's strengths/preferences are not on record yet. fchin should confirm with each before assigning — don't assume.

---

## 6. Verification bar (final SRS audit gate)

Before declaring SRS done:

- [ ] All P0/P1 items from the 2026-07-28 audit are merged.
- [ ] FR-SMS3 (CSV/Excel legacy import) ships with new vitest + e2e.
- [ ] FR-SMS4 / FR-XFER2 sub-paths are reachable and tested.
- [ ] FR-EGOV3 dashboards verified end-to-end (P1-4 closed).
- [ ] Full RBAC matrix sweep (NFR3) shows 0 unexpected leaks.
- [ ] axe-core scan: 0 critical/serious WCAG 2.1 AA violations on every page.
- [ ] Zimbabwe Cyber & Data Protection Act compliance doc signed off.
- [ ] Hardcoded JWT secret fallback removed (or guarded for prod).
- [ ] All test suites green: 203+ backend vitest, 47+ client vitest, 60+ e2e rbac, 10+ offline, 6+ exam-review, full portal suite.
- [ ] Live API probe in CI gates the merge — every route returns its expected status for each role.
- [ ] Spec Deviation doc (NFR1 + NFR5) signed by client.

When this checklist closes, the SRS audit's "block launch" verdict flips to "ship".

---

## 7. Open questions for the client / Lesticom

1. **NFR1 / NFR5 (K8s + GitLab+ArgoCD):** confirmed deviation, or do we need to build it? This is the single biggest open scope question.
2. **Ministry Officer role:** in-scope or out? Affects Phase 4 scope.
3. **Hardcoded JWT secret:** acceptable in dev only? (Current dev fallback is dev-only but should be enforced.)
4. **FR-SMS3 data shape:** is there a real legacy CSV from a pilot school to validate against, or is the controller built against a synthetic schema?

---

## 8. Next step

Recommended: **start with Phase 1, item 1 — merge the P0/P1 PR.** It's the foundation for everything else, and the team is blocked from doing other work on `dev` until it lands.

The work needed to merge:
- Review the two commits (`286480c` + `d597938`) on `fix/p0-srs-compliance`
- Confirm the test evidence in `evidence/post-fix-status.md`
- Open the PR targeting `dev` (not `main` — per AGENTS.md Git Flow)
- Reviewer: fchin
- Estimated review time: 30 min (small diff, big impact, evidence already in the PR)

After merge, pick up Phase 1 items 2–4 in the same work week.

---

*Generated 2026-07-28 against `fix/p0-srs-compliance` @ `d597938`. Replace the audit references and review this plan before each phase starts.*
