Senior Full-Stack / AI Engineer take-home for a multi-portal education platform (Students · Teachers · Parents). This repository contains the 7 hands-on code tasks, the 9 scenario screening answers, and the 4 system-design tasks, implemented in Node.js + TypeScript (the brief permits Node/TS in place of Python/FastAPI).
Runs with zero setup.
npm install && npm test— no API keys, no Docker. Every external service (LLM, Redis, Mongo) is mocked behind an interface by default, with a real-service path behind environment flags.
npm install
npm test # 49 tests across all 7 code tasks
npm run typecheck # strict tsc --noEmit, zero errorsRun an individual task's live demo (all run offline against the mock provider):
npm run demo:task02 # strict JSON schema extraction
npm run demo:task04 # layout-aware chunking
npm run demo:task05 # OCR sanitize + fallback ladder
npm run demo:task07 # resilient gateway: backoff + jitter + model fallback
npm run demo:task10 # request coalescing + batching (130 calls → 4)
npm run demo:task01 # RBAC server → http://localhost:4000 (curl examples printed)
npm run demo:task08 # SSE server → http://localhost:4100 (curl examples printed)To exercise the real Claude API, copy .env.example to .env, set
LLM_PROVIDER=anthropic and ANTHROPIC_API_KEY=..., then re-run any demo.
| # | Task | Type | Location |
|---|---|---|---|
| Part 1 Q1–3 | Isolation / RAG fragmentation / LLM failure | Screening | docs/SCREENING_ANSWERS.md |
| 1 | Multi-Portal RBAC Middleware | Code | src/task01-rbac/ |
| 2 | Strict JSON Schema Extraction | Code | src/task02-schema-extraction/ |
| 3 | Real-Time Inter-Portal Event Chain | Design | docs/SYSTEM_DESIGN.md |
| Part 2 Q1–2 | OCR ordering / vector isolation | Screening | docs/SCREENING_ANSWERS.md |
| 4 | Layout-Aware Ingestion & Chunking | Code | src/task04-chunking/ |
| 5 | Resilient OCR Error Handling | Code | src/task05-ocr-sanitize/ |
| 6 | Secure Document Signing & Storage | Design | docs/SYSTEM_DESIGN.md |
| Part 3 Q1–2 | Surge mitigation / AI rate limiting | Screening | docs/SCREENING_ANSWERS.md |
| 7 | Resilient LLM API Gateway | Code | src/task07-llm-gateway/ |
| 8 | Stream Processing / Real-Time Updates | Code | src/task08-stream/ |
| 9 | GDPR/FERPA Data Lifecycle | Design | docs/SYSTEM_DESIGN.md |
| Part 4 Q1–2 | Double-submit / context cost | Screening | docs/SCREENING_ANSWERS.md |
| 10 | Async Batching & Coalescing Wrapper | Code | src/task10-coalescing/ |
| 11 | Real-Time Token Budgeting Gateway | Design | docs/SYSTEM_DESIGN.md |
Full design rationale for every code task — what's happening, alternatives
considered, and why I rejected them — is in
docs/DEEP_DIVE.md.
- Task 1 — RBAC / IDOR. Identity from a server-validated session (never a client header); a pure object-level authorization policy; 404-not-403 to prevent id enumeration; defense-in-depth row re-check.
- Task 2 — Schema extraction. Forced tool-use for API-level schema constraint, Zod re-validation as the trust boundary, one self-correcting retry, and a safe fallback so the promise never rejects.
- Task 4 — Chunking. Sentence-atomic packing (never severs a sentence), token-based budgeting, overlap, recursive hard-split fallback, and exact vector-DB-ready metadata.
- Task 5 — OCR. Code-point sanitisation + exposed-JSON stripping, a content- quality score, and a confidence-and-structure-gated fallback ladder (passthrough → local cleanup → LLM reconstruct).
- Task 7 — Gateway. Exponential backoff with jitter, transient-only retries, fail-fast on 4xx, cheaper-model fallback, and an attempt-trail error.
- Task 8 — Streaming. HMAC pseudonymisation (+ AES-GCM option), pub/sub fan-out (Redis-swappable), per-owner channel isolation, SSE transport with validated input and leak-free cleanup.
- Task 10 — Coalescing. Batch-by-size-or-time, identical-request coalescing, safe result mapping, mid-flush batch rotation, and a dispatch timeout.
src/
common/llm/ Provider abstraction: interface, Anthropic, mock, scripted, factory
task01-rbac/ Express RBAC endpoint + pure policy + repository
task02-schema-extraction/ Forced-tool extraction + Zod schema + safe fallback
task04-chunking/ Recursive sentence-aware chunker + tokenizer
task05-ocr-sanitize/ Sanitizer + quality scorer + strategy-fallback pipeline
task07-llm-gateway/ ResilientLLMGateway (backoff/jitter/fallback)
task08-stream/ crypto + pubsub + broadcaster + SSE server
task10-coalescing/ RequestCoalescer (batching + coalescing)
tests/ Vitest suites (one per code task, 49 tests)
docs/ SCREENING_ANSWERS.md · SYSTEM_DESIGN.md · DEEP_DIVE.md
Each task folder has a demo.ts runnable via the npm run demo:* scripts.
| Decision | Why | Rejected alternative |
|---|---|---|
| TypeScript (strict) | Type guardrails at the untrusted boundaries this assessment is about; pairs with Zod for runtime validation | Plain JS (loses the guarantees) |
Thin LLMProvider abstraction over Anthropic |
Offline-testable resilience/batching; one-line model fallback; normalised errors | Direct SDK calls (untestable); LangChain (hides the mechanics being probed) |
| Mock-first, real services behind flags | npm test runs instantly with no keys/Docker; interfaces keep the swap honest |
Docker Compose default (higher bar to run) |
| Forced tool-use + Zod (Task 2) | Schema constrained at the API and re-validated locally | Prompt-for-JSON + regex parse (brittle) |
| Sentence-atomic, token-sized chunks (Task 4) | Severing is impossible by construction; sizes match vector limits | Fixed character windows (the cause of the problem) |
| Backoff with jitter (Task 7) | Prevents synchronised thundering-herd retry storms | Plain exponential (clients retry in lockstep) |
| HMAC pseudonymisation (Task 8) | Irreversible, stable, correlation-only — no raw id on the wire | Sending raw ids; reversible encryption when not needed |
The full version of this table, with the reasoning for every task, is in
docs/DEEP_DIVE.md.
The brief evaluates how well generated code is validated and production-proofed.
That loop is part of the deliverable: the implementation was AI-assisted, then
put through a multi-lens adversarial review (security, concurrency,
spec-coverage, production-readiness, TypeScript) with each finding independently
verified before fixing. Ten confirmed findings were applied — including replacing
an ad-hoc crypto KDF with HKDF + production secret enforcement, adding a dispatch
timeout to the coalescer, Zod-validating the streaming endpoint, isolating a
throwing retry callback, and closing an SSE listener leak. Details in
docs/DEEP_DIVE.md §1.5.
All configuration is optional — see .env.example. Highlights:
LLM_PROVIDER—mock(default) oranthropic.ANTHROPIC_API_KEY— required only whenLLM_PROVIDER=anthropic.LLM_PRIMARY_MODEL/LLM_FALLBACK_MODEL— gateway model tiers.STUDENT_ID_HMAC_SECRET— pseudonymisation/encryption secret. A dev default is used whenNODE_ENV !== production; in production it is mandatory (the app refuses to fall back to a built-in value).
No real secrets are committed; .env is git-ignored and .env.example documents
every variable.
- Node.js ≥ 20 (uses
node:cryptoHKDF,randomUUID, native fetch via the SDK). - npm 9+ (
package-lock.jsonis committed for reproducible installs).