geocrop-platform./apps/nextgen/AGENTS.md

38 KiB

AGENTS.md — Africa Alert PWA

Audience. This file is the single source of truth for any AI coding agent (or human contributor) working in this repository. Read it end-to-end before making changes.

Status. Living document. Update it in the same PR that changes the architecture, the coding standards, or the team-agent roster.


1. Project Overview

1.1 Purpose

Africa Alert is a Progressive Web App (PWA) school-management system for African schools. It unifies the four stakeholders of a school — administrators, teachers, students, and parents — into one role-aware interface, and ships with offline support so it works in low-connectivity environments (the primary deployment context is Zimbabwe, based on the Paynow payment-gateway integration at https://www.paynow.co.zw).

1.2 Key Business Goals

Goal What it means in code
Unified school operations One app for student records, classes, attendance, fees, messaging, calendar, exams, payroll, inventory, hostel, transport, and front-office.
Offline-first PWA The React client must install and run without a network. A local SQLite mirror keeps working data available offline; a sync engine reconciles to Supabase when online.
Role-based experience Four user roles (admin / teacher / student / parent) with distinct dashboards, navigation, and permissions.
Cashless fee collection Integration with Paynow Zimbabwe (Ecocash, Visa, etc.) so parents can pay fees online.
Easy deployment One-command docker-compose up for non-technical school operators.

1.3 High-Level Architecture

┌─────────────────────────────────────────────────────────────────────┐
│                         Africa Alert System                         │
└─────────────────────────────────────────────────────────────────────┘

  Browser / Mobile PWA
  ┌──────────────────────────────────────────┐
  │  React 18 + Vite + TypeScript            │
  │  - React Router 6 (role-gated routes)    │
  │  - Zustand stores (auth, api, exams, …)  │
  │  - Axios HTTP client                     │
  │  - vite-plugin-pwa (service worker)      │
  │  - Recharts, Lucide icons                │
  └──────────────┬───────────────────────────┘
                 │ /api/*  (Vite dev proxy :3001)
                 ▼
  ┌──────────────────────────────────────────┐         ┌─────────────────────┐
  │  Node.js 20 + Express API (port 3001)    │  sync   │  Supabase (Postgres)│
  │  - JWT auth (jsonwebtoken)              │ ◀────▶  │  api.next_gen.      │
  │  - bcrypt password hashing               │  30 s   │    techarvest.co.zw  │
  │  - 12 controllers (one per module)       │         └─────────────────────┘
  │  - SQLite (better-sqlite3) local mirror  │                ▲
  │  - SyncEngine (push/pull, server-wins)   │ ───────────────┘
  │  - Paynow integration
  │  - Multer file uploads
  └──────────────────────────────────────────┘
                 │
                 ▼
  ./data/school.db  (SQLite, persistent volume in Docker)

  All in one container via multi-stage Dockerfile + docker-compose.

Architectural style. A classic three-tier monolith (React SPA → Express REST → SQLite) with a bidirectional sync adapter to a managed Postgres (Supabase) for backup and cross-device replication. There is no microservice decomposition; modules are Express Router() instances mounted under /api/<module>.


2. Technology Stack

2.1 Frontend (client/)

  • Language: TypeScript 5.3+
  • Framework: React 18.2 (function components + hooks)
  • Build tool: Vite 5 (vite.config.ts)
  • Routing: react-router-dom 6
  • State management: Zustand 4 (with persist middleware for auth)
  • HTTP client: Axios 1
  • Charts: Recharts 2
  • Icons: lucide-react
  • PWA: vite-plugin-pwa (Workbox runtime caching, autoUpdate)

2.2 Backend (server/)

  • Runtime: Node.js 20 (Alpine in Docker)
  • Framework: Express 4
  • Storage: SQLite via better-sqlite3 12 (WAL mode, foreign_keys = ON)
  • Auth: jsonwebtoken 9 (HS256, 7-day expiry) + bcryptjs 2
  • CORS: cors 2 (currently wide-open; see §6 Security)
  • Uploads: multer 1.4
  • Cloud sync: @supabase/supabase-js 2
  • IDs: uuid 9 (for sync uid columns)
  • Dev loop: nodemon 3

2.3 Database

  • Primary (local): SQLite (single file at server/data/school.db, mode WAL).
  • Replica (cloud): Supabase Postgres. Push/pull through the Supabase REST API.
  • Schema source of truth: server/src/database/init.js (CREATE TABLE IF NOT EXISTS — see §10 for migration plan).
  • Soft-delete + sync metadata on every table:
    last_synced_at DATETIME,
    sync_status   TEXT DEFAULT 'pending' CHECK(sync_status IN ('synced','pending','conflict')),
    is_deleted    INTEGER DEFAULT 0
    

2.4 Infrastructure

  • Containerization: Docker (multi-stage Dockerfileclient-buildserver-buildproduction) + Docker Compose v3.8.
  • Base image: node:20-alpine.
  • Persistent volumes: ./data (SQLite) and ./uploads (file uploads).
  • Ports: 3000 (static client via npx serve), 3001 (API).
  • Entrypoint: docker-entrypoint.sh — runs DB init on first boot, then launches API + static server.

2.5 DevOps

  • CI/CD: None configured. Recommended: GitHub Actions — see §13.
  • Backups: Manual cp -r data data-backup-… (documented in DOCKER_README.md). No automation.
  • Monitoring / health check: Not implemented. Recommended: /api/health endpoint, see §13.
  • Secrets management: Currently via docker-compose.yml env block (with a hardcoded fallback secret — see §6). Recommended: Docker secrets or a .env file with dotenv (NOT committed).

2.6 AI/ML Components

  • None. No ML models, embeddings, or AI services are integrated. The system is purely transactional. If you add AI features (e.g. attendance anomaly detection, grade prediction), add a new sub-section here.

3. Team AI Agents

These are the persistent agent roles this repo expects AI agents to assume. Each role is scoped so two agents can work in parallel without stepping on each other.

3.1 Architect Agent

Owns: client/src/App.tsx, client/vite.config.ts, server/src/index.js, docker-compose.yml, Dockerfile, docker-entrypoint.sh, AGENTS.md.

Responsibilities:

  • System architecture and module boundaries.
  • Scalability (SQLite → Postgres migration, sync sharding, multi-tenant tenancy).
  • Security review (authn, authz, secrets, CORS, rate limiting, payload validation).
  • Design decisions and ADRs (architecture-decision records — see §11).
  • Cross-cutting concerns: error handling, logging, observability.

Out of scope: Implementing feature CRUD — that's the Backend / Frontend agents.

3.2 Backend Agent

Owns: server/src/** (controllers, services, middleware, db), server/package.json.

Responsibilities:

  • Express API development.
  • Business logic, validation, transactions.
  • Database schema and migrations (when extracted from init.js).
  • Sync engine correctness (push/pull ordering, conflict resolution).
  • Backend testing (Jest / Vitest + supertest, once set up — see §13).
  • Paynow integration and webhook security.

Out of scope: React UI, deployment infrastructure.

3.3 Frontend Agent

Owns: client/src/** (components, pages, stores, hooks), client/package.json, client/vite.config.ts, PWA manifest, service-worker config.

Responsibilities:

  • UI / UX implementation.
  • Accessibility (WCAG 2.1 AA — semantic HTML, focus management, ARIA only when needed).
  • State management (Zustand stores) and API caching.
  • Offline / service-worker behavior (Workbox strategies, runtime caching).
  • Frontend testing (Vitest + React Testing Library, once set up — see §13).
  • Form validation and user-facing error handling.

Out of scope: Server endpoints, DB schema.

3.4 DevOps Agent

Owns: Dockerfile, docker-compose.yml, docker-entrypoint.sh, .github/ or .gitlab/ (when created), CI/CD pipelines, backup scripts, deployment docs.

Responsibilities:

  • CI/CD pipelines (lint → typecheck → test → build → deploy).
  • Container hardening (non-root user, healthcheck, multi-stage size optimization).
  • Infrastructure-as-code (when promoted beyond Docker Compose).
  • Monitoring / observability (logs, metrics, traces).
  • Backups (automated SQLite snapshots + restore drills).
  • Secrets management (.env, Docker secrets, vault).

Out of scope: Application logic.

3.5 QA Agent

Owns: tests/ (once created), test plans, regression suites, performance baselines.

Responsibilities:

  • Test plans for each user role (admin / teacher / student / parent).
  • Automated test suite (unit + integration + e2e — Playwright recommended for e2e).
  • Regression testing on every PR.
  • Performance testing (especially the exam timer and bulk attendance endpoints).
  • Accessibility audits (axe-core, manual screen-reader checks).

Out of scope: Writing new feature code; QA flags defects, the owning agent fixes them.

3.6 Documentation Agent

Owns: README.md, DOCKER_README.md, IMPLEMENTATION_SUMMARY.md, SCREENS.md, this file, OpenAPI/Swagger specs (when added), ADRs in docs/adr/.

Responsibilities:

  • Keep README.md in sync with the running app.
  • Maintain SCREENS.md as the canonical UI map (it currently lists screens and the permissions matrix — keep that current as features land).
  • Generate / curate API documentation (OpenAPI from route comments or a swagger-jsdoc setup).
  • Architecture diagrams (use Mermaid in .md files — they render in GitHub / GitLab).
  • Runbooks for common operations (DB reset, sync repair, backup restore).

Out of scope: Writing the code that the docs describe; that is owned by the implementing agent.


4. Coding Standards

4.1 Naming Conventions

Layer Convention Example
React components PascalCase, file matches export AdminDashboard.tsx exports AdminDashboard
Hooks useX camelCase useAuthStore
Zustand stores camelCase, use prefix useExamsStore
TypeScript types / interfaces PascalCase interface User { … }
Express controllers camelCase + .controller.js exams.controller.js
Express services camelCase + .service.js syncEngine.js (existing convention) — keep aligned
Database columns snake_case first_name, sync_status
Environment variables SCREAMING_SNAKE_CASE JWT_SECRET, SUPABASE_URL
Branches feature/<kebab> or fix/<kebab> feature/online-exams, fix/login-redirect

4.2 Folder Organization

  • All backend code under server/src/. Subfolders: controllers/, services/, database/, middleware/, routes/ (when split out).
  • All frontend code under client/src/. Subfolders: components/, pages/, store/, hooks/, utils/, services/.
  • Role-specific pages live under client/src/pages/<role>/ (admin / teacher / student / parent). Cross-role pages stay at client/src/pages/.
  • Shared modules (e.g. an Avatar used by every role) go in client/src/components/, never in a role subfolder.
  • Database artifacts (init.js, future migrations) live in server/src/database/. Do not scatter SQL across controllers.

4.3 Commit Message Format

Use Conventional Commits:

<type>(<scope>): <short imperative summary>

<body — explain WHY, not what>

<footer — breaking changes, issue refs>
  • Types: feat, fix, refactor, docs, test, chore, ci, perf, build.
  • Scope examples: client, server, db, docker, auth, exams, paynow, sync.
  • Example: feat(exams): auto-grade multiple-choice on submit.

4.4 Documentation Requirements

  • Every public module (controller, store, hook, component used across roles) gets a JSDoc / TSDoc header explaining its purpose and one example.
  • Every non-trivial function gets a short JSDoc with @param / @returns for edge cases.
  • Every API route is documented in docs/API.md (or via generated OpenAPI — see §13).
  • Every architectural decision gets an ADR in docs/adr/NNNN-title.md (use docs/adr/0001-record-architecture-decisions.md as the template).

4.5 Testing Requirements

  • Every new controller has at least one integration test (supertest) covering the happy path and one error case.
  • Every new store has a unit test for each action.
  • Every new page has a smoke test (renders without crashing) and an interaction test for the primary user flow.
  • Bug fixes add a regression test that fails before the fix and passes after.
  • Coverage gate: target ≥ 70 % lines on the backend, ≥ 60 % on the frontend (raise over time — see §13).

5. Pull Request Rules

A PR is mergeable only when all of the following are true. The Author is responsible for self-checking; the Reviewer verifies.

5.1 Review Requirements

  • Minimum 1 reviewer for changes under 200 lines, 2 reviewers for ≥ 200 lines or any change touching server/src/index.js, client/src/App.tsx, Dockerfile, docker-compose.yml, or server/src/database/init.js.
  • The owning agent (per §3) must approve changes in their owned area.
  • The Architect agent must approve any change that crosses module boundaries or modifies the auth / sync layer.

5.2 Testing Requirements

  • All new and modified code paths are covered by tests (see §4.5).
  • The full test suite passes locally AND in CI.
  • No tests are skipped or .only left in.

5.3 Security Checks

  • No secrets in code or committed .env files. Verify with git diff --staged before pushing.
  • Any new dependency has been audited (npm audit --omit=dev clean; criticals = 0).
  • Any new endpoint goes through auth and (where applicable) role-check middleware.
  • User-supplied input is validated; raw SQL is parameterized (it already is in this repo — keep that standard).
  • Webhook handlers (Paynow, future Supabase webhooks) verify signatures.

5.4 Documentation Checks

  • README.md and SCREENS.md updated if the user-visible behaviour changes.
  • This AGENTS.md updated if the architecture, standards, or agent roster changes.
  • New API endpoints documented in docs/API.md (or OpenAPI).
  • ADRs added for any non-obvious decision.

5.5 Lint / Typecheck / Build

  • npm run lint (or the configured linter) — 0 errors.
  • npm run typecheck (frontend) — 0 errors.
  • npm run build — succeeds.

6. Definition of Done

A user story is Done when all of the following hold:

  1. Code builds successfullydocker-compose build (or npm run build in each subproject) succeeds.
  2. Tests pass — full test suite green, coverage meets the gate in §4.5.
  3. Documentation updated — README, SCREENS, and AGENTS touched where applicable.
  4. Security checks completed — secrets scan clean, deps audited, auth in place, input validated.
  5. Linting passes — 0 lint errors, 0 type errors.
  6. Demo verified — the change has been exercised end-to-end against a running dev environment.
  7. PR merged — at least one approval, CI green, branch deleted.

7. Development Workflow

7.1 Branch Strategy

This repo follows a strict two-branch model — Git Flow style, with main as production.

Branch Purpose Pushable from Invariant
dev Work and test. All day-to-day work, new features, refactors, and bug fixes land here. The integration branch for in-progress code. Any local commit / merge May be broken. May have unreleased features.
main Production. Only receives code that has been tested on dev. Every commit on main MUST be deployable. A green test run against dev, followed by a deliberate merge Must always be deployable. No broken builds, no half-finished features, no WIP commits.
feature/<kebab> A new feature. Branched from dev. Merged back into dev (PR).
fix/<kebab> A bug fix. Branched from dev. Merged back into dev (PR).
hotfix/<kebab> An emergency fix that must reach production without going through the full dev test cycle. Branched from main. Merged into both main and dev so dev doesn't drift. Used sparingly.

Promotion path: devmain. Promote only after:

  1. All required tests pass on dev (whatever the team has agreed — currently there are no automated tests, so this is at minimum: manual smoke + a green docker-compose up). The audit recommends a real test suite — see §13.4.
  2. The dev tip is reviewed and accepted.
  3. A human (the maintainer) merges dev into main with a deliberate commit (no fast-forward). Tag the resulting main tip as a release (see §7.3).

Why this matters. If you commit straight to main, or merge an untested feature into main, you break the "always deployable" invariant. Subsequent deployments will ship broken code. Treat any direct commit to main as a release-blocker.

Branch names: kebab-case, ≤ 50 chars, no prefixes like my/. Use the worktree-management skill — every code change goes in .worktrees/feature-<name>/ (or fix-<name>, hotfix-<name>).

7.2 PR Workflow

  1. Pick or open an issue describing the change.
  2. Branch from dev (not main) unless this is a hotfix (§7.1).
  3. Implement + test locally.
  4. Run the Definition of Done checklist (§6).
  5. Open a PR targeting dev. Title in Conventional Commits format. Description links the issue and lists the user-visible behaviour change.
  6. Address review feedback.
  7. Merge via squash (or whatever the team agrees on). Delete the source branch.
  8. Promote to main separately, after a green test run on dev (§7.1).

7.3 Release Workflow

  • main is the source of release tags. When you promote devmain and that merge is a release candidate:
    • Tag the new main tip as vMAJOR.MINOR.PATCH (semver).
    • Maintain a CHANGELOG.md per release (group: Added / Changed / Fixed / Removed).
  • Backwards-incompatible changes bump MAJOR and write an ADR + migration guide.

7.4 Deployment Workflow

  • Container image is the unit of deployment. Dockerfile produces one image that runs both API and static client.
  • docker-compose up -d on the target host. Persistent volumes: data/, uploads/.
  • Pre-deploy checklist:
    • DB backup taken (cp -r data data-backup-$(date +%F)).
    • .env file present and validated (no missing required vars).
    • Image tag matches the release tag.
  • Post-deploy: run smoke tests, verify /api/health (once added), check sync status (GET /api/sync/status).

8. Security Guidelines

8.1 Secret Management

  • Never commit secrets. The current repo already has a hardcoded JWT secret in docker-compose.yml (africa-alert-production-secret-2024) and in server/src/index.js (africa-alert-secret-key-2024). Action required — see §13.3.
  • Use environment variables. Provide an .env.example (NOT .env) that lists every required var.
  • Production secrets live in a secret manager (Docker secrets, HashiCorp Vault, cloud KMS). Rotate JWT secrets at least quarterly.
  • The Supabase service key and Paynow integration key are particularly sensitive — never log them, never return them to the client.

8.2 Dependency Scanning

  • npm audit --omit=dev on every PR. Block on any high or critical CVE.
  • Dependabot / Renovate configured to open weekly PRs.
  • Pin major versions in package.json; let package-lock.json pin exact.

8.3 Authentication Standards

  • All /api/* routes (except /api/auth/login and /api/auth/register) require a valid JWT in the Authorization: Bearer <token> header.
  • Passwords stored with bcrypt (cost factor ≥ 10). Never store plaintext.
  • Tokens expire in 7 days (current setting). Add refresh-token flow for longer sessions.
  • Rate-limit /api/auth/login (e.g. express-rate-limit, 5 attempts per 15 min per IP) to slow credential stuffing.
  • Invalidate tokens on logout (blocklist or short-lived access tokens + refresh tokens).

8.4 Authorization Standards

  • Every endpoint must check both authentication (auth middleware) and role where applicable.
  • Use a role-based check helper: requireRole('admin', 'teacher') — do not sprinkle if (req.user.role !== 'admin') across controllers (the codebase already has this duplication — see §13.2).
  • Never trust client-supplied user IDs. Always read req.user.id for the "current user".
  • Resource-level access (a student reading their own grade) must be enforced in the controller, not only in the UI.

9. AI Collaboration Rules

These rules apply to every AI agent (Mavis rein, GitHub Copilot, or any other tool) working in this repo. They are non-negotiable.

  1. Do not modify unrelated files. Stay inside the area you own (§3). Cross-cutting changes require an Architect-agent review.
  2. Explain major architectural changes in an ADR before writing code. Surface the trade-offs in the PR description.
  3. Create tests for every new feature in the same change. No untested code lands on dev.
  4. Update documentation when changing functionality. README, SCREENS, AGENTS, and the API doc all reflect what the code actually does.
  5. Prefer maintainability over cleverness. A 50-line explicit controller beats a 10-line abstract one that nobody can debug at 2am.
  6. Ask before destructive changes. Deleting files, force-pushing, dropping tables, hard-resetting the DB — always confirm with the user first.
  7. Read this file end-to-end before your first change in the repo. If anything is unclear or out of date, flag it in the PR.
  8. Use the worktree workflow. All code changes go in .worktrees/feature-<name>/ — never in the main checkout. See worktree-management skill.
  9. Reference code with path:line when discussing it in chat or PRs — never paste a wall of context.
  10. Be conservative with new dependencies. Each new package.json entry increases the supply-chain surface. Justify it.

10. Repository Knowledge Base

A condensed map of the codebase. New agents: read this section first.

10.1 Important Modules

Module Frontend Backend Controller Notes
Auth client/src/store/auth.ts POST /api/auth/login, /register in server/src/index.js JWT + bcrypt; 7-day expiry
Users client/src/pages/Students.tsx, Teachers.tsx, admin/Users.tsx controllers/users.controller.js Roles: admin / teacher / student / parent (+ accountant / librarian / nurse in schema)
Classes & Subjects client/src/pages/Classes.tsx inline in server/src/index.js (classes + subjects)
Enrollments inline in server/src/index.js
Attendance client/src/pages/Attendance.tsx inline + controllers/attendance.controller.js Supports bulk upsert
Fees client/src/pages/Fees.tsx inline + controllers/paynow.controller.js Paynow integration for online payment
Messages client/src/pages/Messages.tsx controllers/messages.controller.js
Calendar / Events client/src/pages/Events.tsx controllers/calendar.controller.js
Exams client/src/pages/exams/ExamViews.tsx + client/src/store/exams.ts controllers/exams.controller.js Full exam lifecycle: groups → questions → schedules → attempts → grading
Assignments client/src/store/assignments.ts controllers/assignments.controller.js
Grades client/src/pages/student/Grades.tsx controllers/grades.controller.js
Reports client/src/pages/admin/Reports.tsx controllers/reports.controller.js
Settings client/src/pages/admin/Settings.tsx controllers/settings.controller.js
Departments client/src/pages/admin/Departments.tsx controllers/departments.controller.js
Payroll & HR (frontend TBD) DB only (init.js) 8 tables: staff_roles, salary_grades, staff_records, leave_types, leave_requests, staff_attendance, payroll_runs, payslips
Inventory (frontend TBD) DB only 7 tables: item_categories, suppliers, store_locations, items, item_stock, stock_transactions, item_issues
Hostel (frontend TBD) DB only 6 tables
Transport (frontend TBD) DB only 5 tables
Front Office (frontend TBD) DB only 5 tables
Sync services/SyncEngine.js Bidirectional, server-wins conflict policy, 30 s interval

10.2 Key Services

  • SyncEngine (server/src/services/SyncEngine.js) — singleton (getSyncEngine()). Runs every SYNC_INTERVAL ms (default 30000). Pushes local sync_status = 'pending' rows to Supabase REST, pulls remote updated_at >= lastSync rows back. Logs errors to sync_logs. Exposes /api/sync/status and /api/sync/force.
  • Paynow client (server/src/controllers/paynow.controller.js) — integrates with https://www.paynow.co.zw/interface/initiatetransaction. Endpoints: POST /initiate, GET /status/:id, POST /webhook, GET /student/:id, DELETE /:id.
  • Auth — JWT signed with JWT_SECRET (env), 7-day expiry. Role embedded in payload.

10.3 Database Schema (39+ tables)

Full schema lives in server/src/database/init.js (1675 lines). All tables share the sync columns: uid, last_synced_at, sync_status, is_deleted. Core groups:

  • Core (11): users, classes, subjects, enrollments, attendance, fee_groups, student_fees, payments, messages, events, expenses, grades, sync_logs, sync_config, system_settings.
  • Payroll & HR (8): staff_roles, salary_grades, staff_records, leave_types, leave_requests, staff_attendance, payroll_runs, payslips.
  • Inventory (7): item_categories, suppliers, store_locations, items, item_stock, stock_transactions, item_issues.
  • Exams (5): exam_groups, question_banks, exam_schedules, exam_attempts, exam_answers.
  • Hostel (6): hostels, room_types, rooms, room_assignments, hostel_fees.
  • Transport (5): vehicles, routes, pickup_points, vehicle_routes, transport_allocations.
  • Front Office (5): admission_enquiries, visitor_logs, phone_call_logs, postal_dispatch, complaints.

User roles supported by the schema CHECK constraint: admin, teacher, student, parent, accountant, librarian, nurse. The frontend currently only authenticates the first four.

10.4 API Structure

  • All routes mounted under /api/<module> in server/src/index.js.
  • 12 controller routers + 2 inline route blocks (auth, dashboard stats) + 2 inline CRUD blocks (classes, subjects, enrollments, attendance, fees, messages, events, expenses).
  • Response shape: JSON. Errors are { "error": "<message>" } (no error code, no stack trace in production — see §13.4).
  • All authenticated routes set req.user = { id, email, role } from the JWT.

10.5 Infrastructure Components

  • Dockerfile — multi-stage: client-build (Vite build) → server-build (npm ci) → production (Alpine runtime, copies built artefacts, runs docker-entrypoint.sh).
  • docker-compose.yml — single app service. Mounts ./data and ./uploads as volumes. Exposes 3000 (client) and 3001 (API). Currently hardcodes JWT_SECRET — see §13.3.
  • docker-entrypoint.sh — initializes DB on first run, starts API in background, then npx serve for the static client.

10.6 Environment Variables

Name Default Required Purpose
PORT 3001 no API port
JWT_SECRET ⚠️ hardcoded fallback yes (prod) Signs JWTs. MUST be set in production.
DB_PATH ./data/school.db no SQLite path
SUPABASE_URL https://api.next_gen.techarvest.co.zw no Cloud sync target
SUPABASE_KEY empty no (offline mode if missing) Cloud sync auth
SYNC_INTERVAL 30000 no Sync period in ms
PAYNOW_INTEGRATION_ID empty yes (prod) Paynow merchant id
PAYNOW_INTEGRATION_KEY empty yes (prod) Paynow signing key
PAYNOW_RETURN_URL http://localhost:3000/fees no Post-payment redirect
PAYNOW_BLOCKING_URL http://localhost:3000/api/payments/webhook no Webhook target
NODE_ENV production (in Docker) no Standard Node env

11. Architecture Decision Records (ADRs)

Place new ADRs in docs/adr/NNNN-<title>.md. Use MADR or a simplified template:

# NNNN. <Title>

- **Status:** Proposed | Accepted | Superseded by NNNN
- **Date:** YYYY-MM-DD
- **Deciders:** <agents / humans>

## Context
<what's the situation>

## Decision
<what we chose>

## Consequences
<what becomes easier, what becomes harder>

Suggested first ADRs to write:

  1. 0001 — Use SQLite as the local mirror + Supabase as cloud (current model).
  2. 0002 — Server-wins conflict resolution in sync.
  3. 0003 — JWT-only auth (no session cookies).
  4. 0004 — Single-container deployment.
  5. 0005 — Role-based access (admin / teacher / student / parent).

12. Onboarding Checklist (for any new agent)

Use this when picking up the repo fresh:

  • Read this AGENTS.md end-to-end.
  • Skim README.md, SCREENS.md, IMPLEMENTATION_SUMMARY.md, DOCKER_README.md.
  • Run docker-compose up -d and log in as admin@school.com / admin123.
  • Click through every dashboard and module once.
  • Read server/src/index.js and server/src/services/SyncEngine.js.
  • Read client/src/App.tsx and client/src/store/auth.ts.
  • Read server/src/database/init.js (it's long — focus on the CHECK constraints and the sync columns).
  • Skim one controller per module (e.g. exams.controller.js, paynow.controller.js).
  • Run the dev servers in worktrees per the worktree-management skill.
  • Pick a small, scoped issue from the backlog and follow the PR workflow in §7.

13. Recommendations (action items for the human maintainer)

The audit identified the following. Treat them as a prioritised backlog — not a demand. Every recommendation has a one-line rationale.

13.1 Architecture Improvements (priority: medium → high)

  • M1 — Move schema out of init.js into versioned migrations (use node-pg-migrate style or knex). init.js is 1675 lines and grows linearly with every new module. Why: current CREATE TABLE IF NOT EXISTS pattern cannot represent ALTER statements, so any schema change is a full DB rebuild.
  • M2 — Extract a shared auth middleware module. Each controller currently re-declares its own auth (e.g. exams.controller.js:17, paynow.controller.js:18, index.js:55). Why: drift risk — a security fix to one will be missed in the others.
  • M3 — Introduce a service layer between controllers and the DB. Right now controllers inline SQL strings. Extract users.service.js, exams.service.js, etc. Why: testability (mock the service, not the DB) and reuse (the sync engine will share services).
  • M4 — Add request validation. Use zod or joi schemas at the route boundary. Why: the current code trusts req.body to have the right shape; a malformed payload currently throws inside better-sqlite3.
  • M5 — Make the sync engine resilient to a missing/disrupted Supabase. It already no-ops on missing key; harden the timeout, retry, and circuit-breaker paths. Why: when sync is down, the API must not slow down.
  • M6 — Multi-tenant design (if applicable). If multiple schools will use one deployment, add a school_id to every table and a tenant-scoping middleware. Why: retrofitting tenant IDs is a painful migration.

13.2 Missing Documentation (priority: medium)

  • D1 — docs/API.md (or docs/openapi.yaml). No central API reference. Generate from JSDoc with swagger-jsdoc + swagger-ui-express.
  • D2 — docs/adr/ with the suggested ADRs from §11.
  • D3 — .env.example at repo root listing every variable from §10.6.
  • D4 — Operations runbook (docs/runbook.md): how to reset the DB, repair a broken sync, restore from backup, rotate secrets.
  • D5 — Document the schema in docs/schema.md (Mermaid ER diagram is fine).
  • D6 — Document the scopes / permissions in a code-readable form (a JSON permissions.json that the server can import), so frontend and backend share one source of truth. Currently the matrix lives only in SCREENS.md and is duplicated in App.tsx and Nav.tsx.

13.3 Security Improvements (priority: HIGH)

  • S1 — Remove the hardcoded JWT secret fallbacks. server/src/index.js:25 and every controller that does process.env.JWT_SECRET || 'africa-alert-secret-key-2024'. Refuse to start without JWT_SECRET in production. Why: anyone reading the public repo can mint admin JWTs.
  • S2 — Remove the hardcoded JWT_SECRET from docker-compose.yml (line 18). It is currently africa-alert-production-secret-2024 — a production-named secret committed to git. Why: it is now public knowledge. Rotate immediately in any environment that ever used this value.
  • S3 — Add express-rate-limit to /api/auth/login and /api/auth/register. No throttling today.
  • S4 — Tighten CORS. app.use(cors()) is wide-open (server/src/index.js:36). Restrict to known origins.
  • S5 — Add a request-size limit (express.json({ limit: '1mb' })). Default is 100kb, but be explicit.
  • S6 — Verify Paynow webhook signatures. Currently the webhook handler accepts any payload; the hash check appears in generateHash but is it verified on inbound? Audit paynow.controller.js end-to-end.
  • S7 — Add helmet for sensible HTTP security headers.
  • S8 — Scan dependencies in CI (npm audit + a Dependabot config).

13.4 Testing Improvements (priority: medium → high)

  • T1 — Add a test runner to both packages. No tests exist today. Backend: Jest + supertest. Frontend: Vitest + React Testing Library + @testing-library/jest-dom. E2E: Playwright.
  • T2 — Write integration tests for the auth flow first (login, bad password, expired token, role denial).
  • T3 — Write integration tests for the exam timer + auto-submit (race-condition-prone).
  • T4 — Write a regression test for the sync engine's conflict resolution (server-wins).
  • T5 — Add a coverage gate in CI (see §4.5).
  • T6 — Add a smoke test for the Docker image — start the container, hit /api/health (once added), assert 200.

13.5 DevOps Improvements (priority: medium)

  • O1 — Add /api/health and /api/ready endpoints. Liveness vs readiness (the latter should return 503 until the DB is initialized).
  • O2 — Add CI (GitHub Actions recommended): install → lint → typecheck → test → build → docker build.
  • O3 — Pin Node version in .nvmrc and in Dockerfile (FROM node:20.x-alpine). Pinning to node:20-alpine is OK but explicit 20.x.y is safer.
  • O4 — Add a non-root USER in the production Dockerfile stage. Containers currently run as root.
  • O5 — Add a HEALTHCHECK instruction to the Dockerfile.
  • O6 — Automate SQLite backups (cron sidecar or a simple node-cron task in the API). Ship a documented restore.sh.
  • O7 — Add log aggregation. Today logs go to stdout only. Pipe to a log shipper (Loki, CloudWatch, etc.) in production.
  • O8 — Add .dockerignore to keep node_modules, data/, dist/, uploads/, .git/, .harness/ out of the build context.
  • O9 — Split the Docker image into client (Nginx serving dist/) + api for cleaner scaling. Optional — current single-image design is simpler to operate.

13.6 Documentation-as-Product (priority: low → medium)

  • P1 — Promote SCREENS.md to a generated artefact. It is already excellent; consider a small script that builds it from the route table.
  • P2 — Add a CONTRIBUTING.md linking to this file and the worktree workflow.
  • P3 — Add a SECURITY.md with a disclosure address (e.g. security@…).

14. Open Questions for the Maintainer

These are decisions the audit surfaced that only you can answer. Each is one PR away from being resolved.

  1. Which branch is the integration target — dev or main? Resolved 2026-06-03dev is for work + test, main is for deployable code only. Documented in §7.1.
  2. Do you plan to scale beyond a single SQLite file? If so, when — that determines whether to invest in the migration to Postgres now or in 6 months.
  3. Are accountant, librarian, nurse (defined in the schema) planned roles? They have no UI today.
  4. Will the same deployment serve multiple schools, or one-school-per-install?
  5. Who hosts the Supabase project, and who rotates the service key?
  6. Is there a separate staging environment, or does docker-compose up on a developer laptop serve as staging?
  7. What is the on-call story for a school that loses connectivity during exam day?
  8. Are there any school-data privacy requirements I should encode into the architecture (e.g. POPIA, GDPR, local equivalents)?

Document version: 1.0 — written 2026-06-03 by an initial repository audit. Update in the same PR as any change to the architecture, the standards, or the agent roster.